near_api/common/
send.rs

1use std::sync::Arc;
2
3use near_openapi_client::types::{
4    ErrorWrapperForRpcTransactionError, FinalExecutionOutcomeView, JsonRpcRequestForSendTx,
5    JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError, RpcSendTransactionRequest,
6    RpcTransactionError, RpcTransactionResponse,
7};
8
9use near_api_types::{
10    transaction::{
11        delegate_action::{SignedDelegateAction, SignedDelegateActionAsBase64},
12        result::ExecutionFinalResult,
13        PrepopulateTransaction, SignedTransaction,
14    },
15    BlockHeight, CryptoHash, Nonce, PublicKey, TxExecutionStatus,
16};
17use reqwest::Response;
18use tracing::{debug, info};
19
20use crate::{
21    common::utils::{is_critical_transaction_error, to_retry_error},
22    config::{retry, NetworkConfig, RetryResponse},
23    errors::{
24        ArgumentValidationError, ExecuteMetaTransactionsError, ExecuteTransactionError,
25        MetaSignError, SendRequestError, SignerError, ValidationError,
26    },
27    signer::Signer,
28};
29
30use super::META_TRANSACTION_VALID_FOR_DEFAULT;
31
32const TX_EXECUTOR_TARGET: &str = "near_api::tx::executor";
33const META_EXECUTOR_TARGET: &str = "near_api::meta::executor";
34
35#[async_trait::async_trait]
36pub trait Transactionable: Send + Sync {
37    fn prepopulated(&self) -> Result<PrepopulateTransaction, ArgumentValidationError>;
38
39    /// Validate the transaction before sending it to the network
40    async fn validate_with_network(&self, network: &NetworkConfig) -> Result<(), ValidationError>;
41
42    /// Edit the transaction before sending it to the network.
43    /// This is useful for example to add storage deposit to the transaction
44    /// if it's needed.
45    /// Though, it won't be called if the user has presigned the transaction.
46    async fn edit_with_network(&mut self, _network: &NetworkConfig) -> Result<(), ValidationError> {
47        Ok(())
48    }
49}
50
51pub enum TransactionableOrSigned<Signed> {
52    /// A transaction that is not signed.
53    Transactionable(Box<dyn Transactionable + 'static>),
54    /// A transaction that is signed and ready to be sent to the network.
55    Signed((Signed, Box<dyn Transactionable + 'static>)),
56}
57
58impl<Signed> TransactionableOrSigned<Signed> {
59    pub fn signed(self) -> Option<Signed> {
60        match self {
61            Self::Signed((signed, _)) => Some(signed),
62            Self::Transactionable(_) => None,
63        }
64    }
65}
66
67impl<S> TransactionableOrSigned<S> {
68    pub fn transactionable(self) -> Box<dyn Transactionable> {
69        match self {
70            Self::Transactionable(transaction) => transaction,
71            Self::Signed((_, transaction)) => transaction,
72        }
73    }
74}
75
76/// The handler for signing and sending transaction to the network.
77///
78/// This is the main entry point for the transaction sending functionality.
79pub struct ExecuteSignedTransaction {
80    /// The transaction that is either not signed yet or already signed.
81    pub transaction: TransactionableOrSigned<SignedTransaction>,
82    /// The signer that will be used to sign the transaction.
83    pub signer: Arc<Signer>,
84
85    pub wait_until: TxExecutionStatus,
86}
87
88impl ExecuteSignedTransaction {
89    pub fn new<T: Transactionable + 'static>(transaction: T, signer: Arc<Signer>) -> Self {
90        Self {
91            transaction: TransactionableOrSigned::Transactionable(Box::new(transaction)),
92            signer,
93            wait_until: TxExecutionStatus::Final,
94        }
95    }
96
97    /// Changes the transaction to a [meta transaction](https://docs.near.org/concepts/abstraction/meta-transactions), allowing some 3rd party entity to execute it and
98    /// pay for the gas.
99    ///
100    /// Please note, that if you already presigned the transaction, it would require you to sign it again as a meta transaction
101    /// is a different type of transaction.
102    pub fn meta(self) -> ExecuteMetaTransaction {
103        ExecuteMetaTransaction::from_box(self.transaction.transactionable(), self.signer)
104    }
105
106    pub const fn wait_until(mut self, wait_until: TxExecutionStatus) -> Self {
107        self.wait_until = wait_until;
108        self
109    }
110
111    /// Signs the transaction offline without fetching the nonce or block hash from the network.
112    ///
113    /// The transaction won't be broadcasted to the network and just stored signed in the [Self::transaction] struct variable.
114    ///
115    /// This is useful if you already have the nonce and block hash, or if you want to sign the transaction
116    /// offline. Please note that incorrect nonce will lead to transaction failure.
117    pub async fn presign_offline(
118        mut self,
119        public_key: PublicKey,
120        block_hash: CryptoHash,
121        nonce: Nonce,
122    ) -> Result<Self, ExecuteTransactionError> {
123        let transaction = match &self.transaction {
124            TransactionableOrSigned::Transactionable(transaction) => transaction,
125            TransactionableOrSigned::Signed(_) => return Ok(self),
126        };
127
128        let transaction = transaction.prepopulated()?;
129
130        let signed_tr = self
131            .signer
132            .sign(transaction, public_key, nonce, block_hash)
133            .await?;
134
135        self.transaction =
136            TransactionableOrSigned::Signed((signed_tr, self.transaction.transactionable()));
137        Ok(self)
138    }
139
140    /// Signs the transaction with the custom network configuration but doesn't broadcast it.
141    ///
142    /// Signed transaction is stored in the [Self::transaction] struct variable.
143    ///
144    /// This is useful if you want to sign with non-default network configuration (e.g, custom RPC URL, sandbox).
145    /// The provided call will fetch the nonce and block hash from the given network.
146    pub async fn presign_with(
147        self,
148        network: &NetworkConfig,
149    ) -> Result<Self, ExecuteTransactionError> {
150        let transaction = match &self.transaction {
151            TransactionableOrSigned::Transactionable(transaction) => transaction,
152            TransactionableOrSigned::Signed(_) => return Ok(self),
153        };
154
155        let transaction = transaction.prepopulated()?;
156
157        let signer_key = self
158            .signer
159            .get_public_key()
160            .await
161            .map_err(SignerError::from)?;
162        let (nonce, hash, _) = self
163            .signer
164            .fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
165            .await
166            .map_err(MetaSignError::from)?;
167        self.presign_offline(signer_key, hash, nonce).await
168    }
169
170    /// Signs the transaction with the default mainnet configuration. Does not broadcast it.
171    ///
172    /// Signed transaction is stored in the [Self::transaction] struct variable.
173    ///
174    /// The provided call will fetch the nonce and block hash from the network.
175    pub async fn presign_with_mainnet(self) -> Result<Self, ExecuteTransactionError> {
176        let network = NetworkConfig::mainnet();
177        self.presign_with(&network).await
178    }
179
180    /// Signs the transaction with the default testnet configuration. Does not broadcast it.
181    ///
182    /// Signed transaction is stored in the [Self::transaction] struct variable.
183    ///
184    /// The provided call will fetch the nonce and block hash from the network.
185    pub async fn presign_with_testnet(self) -> Result<Self, ExecuteTransactionError> {
186        let network = NetworkConfig::testnet();
187        self.presign_with(&network).await
188    }
189
190    /// Sends the transaction to the custom provided network.
191    ///
192    /// This is useful if you want to send the transaction to a non-default network configuration (e.g, custom RPC URL, sandbox).
193    /// Please note that if the transaction is not presigned, it will be signed with the network's nonce and block hash.
194    pub async fn send_to(
195        mut self,
196        network: &NetworkConfig,
197    ) -> Result<ExecutionFinalResult, ExecuteTransactionError> {
198        let (signed, transactionable) = match &mut self.transaction {
199            TransactionableOrSigned::Transactionable(transaction) => {
200                debug!(target: TX_EXECUTOR_TARGET, "Preparing unsigned transaction");
201                (None, transaction)
202            }
203            TransactionableOrSigned::Signed((s, transaction)) => {
204                debug!(target: TX_EXECUTOR_TARGET, "Using pre-signed transaction");
205                (Some(s.clone()), transaction)
206            }
207        };
208
209        let wait_until = self.wait_until;
210
211        if signed.is_none() {
212            debug!(target: TX_EXECUTOR_TARGET, "Editing transaction with network config");
213            transactionable.edit_with_network(network).await?;
214        } else {
215            debug!(target: TX_EXECUTOR_TARGET, "Validating pre-signed transaction with network config");
216            transactionable.validate_with_network(network).await?;
217        }
218
219        let signed = match signed {
220            Some(s) => s,
221            None => {
222                debug!(target: TX_EXECUTOR_TARGET, "Signing transaction");
223                self.presign_with(network)
224                    .await?
225                    .transaction
226                    .signed()
227                    .expect("Expect to have it signed")
228            }
229        };
230
231        info!(
232            target: TX_EXECUTOR_TARGET,
233            "Broadcasting signed transaction. Hash: {:?}, Signer: {:?}, Receiver: {:?}, Nonce: {}",
234            signed.get_hash(),
235            signed.transaction.signer_id(),
236            signed.transaction.receiver_id(),
237            signed.transaction.nonce(),
238        );
239
240        Self::send_impl(network, signed, wait_until).await
241    }
242
243    /// Sends the transaction to the default mainnet configuration.
244    ///
245    /// Please note that this will sign the transaction with the mainnet's nonce and block hash if it's not presigned yet.
246    pub async fn send_to_mainnet(self) -> Result<ExecutionFinalResult, ExecuteTransactionError> {
247        let network = NetworkConfig::mainnet();
248        self.send_to(&network).await
249    }
250
251    /// Sends the transaction to the default testnet configuration.
252    ///
253    /// Please note that this will sign the transaction with the testnet's nonce and block hash if it's not presigned yet.
254    pub async fn send_to_testnet(self) -> Result<ExecutionFinalResult, ExecuteTransactionError> {
255        let network = NetworkConfig::testnet();
256        self.send_to(&network).await
257    }
258
259    async fn send_impl(
260        network: &NetworkConfig,
261        signed_tr: SignedTransaction,
262        wait_until: TxExecutionStatus,
263    ) -> Result<ExecutionFinalResult, ExecuteTransactionError> {
264        let hash = signed_tr.get_hash();
265        let signed_tx_base64: near_openapi_client::types::SignedTransaction = signed_tr.into();
266        let result = retry(network.clone(), |client| {
267            let signed_tx_base64 = signed_tx_base64.clone();
268            async move {
269                let result = match client
270                    .send_tx(&JsonRpcRequestForSendTx {
271                        id: "0".to_string(),
272                        jsonrpc: "2.0".to_string(),
273                        method: near_openapi_client::types::JsonRpcRequestForSendTxMethod::SendTx,
274                        params: RpcSendTransactionRequest {
275                            signed_tx_base64,
276                            wait_until,
277                        },
278                    })
279                    .await
280                    .map(|r| r.into_inner())
281                    .map_err(SendRequestError::from)
282                {
283                    Ok(
284                        JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError::Variant0 {
285                            result,
286                            ..
287                        },
288                    ) => RetryResponse::Ok(result),
289                    Ok(
290                        JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError::Variant1 {
291                            error,
292                            ..
293                        },
294                    ) => {
295                        let error: SendRequestError<RpcTransactionError> =
296                            SendRequestError::from(error);
297                        to_retry_error(error, is_critical_transaction_error)
298                    }
299                    Err(err) => to_retry_error(err, is_critical_transaction_error),
300                };
301
302                tracing::debug!(
303                    target: TX_EXECUTOR_TARGET,
304                    "Broadcasting transaction {} resulted in {:?}",
305                    hash,
306                    result
307                );
308
309                result
310            }
311        })
312        .await
313        .map_err(ExecuteTransactionError::TransactionError)?;
314
315        // TODO: check if we need to add support for that final_execution_status
316        let final_execution_outcome_view = match result {
317            // We don't use `experimental_tx`, so we can ignore that, but just to be safe
318            RpcTransactionResponse::Variant0 {
319                final_execution_status: _,
320                receipts: _,
321                receipts_outcome,
322                status,
323                transaction,
324                transaction_outcome,
325            } => FinalExecutionOutcomeView {
326                receipts_outcome,
327                status,
328                transaction,
329                transaction_outcome,
330            },
331            RpcTransactionResponse::Variant1 {
332                final_execution_status: _,
333                receipts_outcome,
334                status,
335                transaction,
336                transaction_outcome,
337            } => FinalExecutionOutcomeView {
338                receipts_outcome,
339                status,
340                transaction,
341                transaction_outcome,
342            },
343        };
344
345        Ok(ExecutionFinalResult::try_from(
346            final_execution_outcome_view,
347        )?)
348    }
349}
350
351pub struct ExecuteMetaTransaction {
352    pub transaction: TransactionableOrSigned<SignedDelegateAction>,
353    pub signer: Arc<Signer>,
354    pub tx_live_for: Option<BlockHeight>,
355}
356
357impl ExecuteMetaTransaction {
358    pub fn new<T: Transactionable + 'static>(transaction: T, signer: Arc<Signer>) -> Self {
359        Self {
360            transaction: TransactionableOrSigned::Transactionable(Box::new(transaction)),
361            signer,
362            tx_live_for: None,
363        }
364    }
365
366    pub fn from_box(transaction: Box<dyn Transactionable + 'static>, signer: Arc<Signer>) -> Self {
367        Self {
368            transaction: TransactionableOrSigned::Transactionable(transaction),
369            signer,
370            tx_live_for: None,
371        }
372    }
373
374    /// Sets the transaction live for the given block amount.
375    ///
376    /// This is useful if you want to set the transaction to be valid for a specific amount of blocks.\
377    /// The default amount is 1000 blocks.
378    pub const fn tx_live_for(mut self, tx_live_for: BlockHeight) -> Self {
379        self.tx_live_for = Some(tx_live_for);
380        self
381    }
382
383    /// Signs the transaction offline without fetching the nonce or block hash from the network. Does not broadcast it.
384    ///
385    /// Signed transaction is stored in the [Self::transaction] struct variable.
386    ///
387    /// This is useful if you already have the nonce and block hash, or if you want to sign the transaction
388    /// offline. Please note that incorrect nonce will lead to transaction failure and incorrect block height
389    /// will lead to incorrectly populated transaction live value.
390    pub async fn presign_offline(
391        mut self,
392        signer_key: PublicKey,
393        block_hash: CryptoHash,
394        nonce: Nonce,
395        block_height: BlockHeight,
396    ) -> Result<Self, ExecuteMetaTransactionsError> {
397        let transaction = match &self.transaction {
398            TransactionableOrSigned::Transactionable(transaction) => transaction,
399            TransactionableOrSigned::Signed(_) => return Ok(self),
400        };
401
402        let transaction = transaction.prepopulated()?;
403        let max_block_height = block_height
404            + self
405                .tx_live_for
406                .unwrap_or(META_TRANSACTION_VALID_FOR_DEFAULT);
407
408        let signed_tr = self
409            .signer
410            .sign_meta(transaction, signer_key, nonce, block_hash, max_block_height)
411            .await?;
412
413        self.transaction =
414            TransactionableOrSigned::Signed((signed_tr, self.transaction.transactionable()));
415        Ok(self)
416    }
417
418    /// Signs the transaction with the custom network configuration but doesn't broadcast it.
419    ///
420    /// Signed transaction is stored in the [Self::transaction] struct variable.
421    ///
422    /// This is useful if you want to sign with non-default network configuration (e.g, custom RPC URL, sandbox).
423    pub async fn presign_with(
424        self,
425        network: &NetworkConfig,
426    ) -> Result<Self, ExecuteMetaTransactionsError> {
427        let transaction = match &self.transaction {
428            TransactionableOrSigned::Transactionable(transaction) => transaction,
429            TransactionableOrSigned::Signed(_) => return Ok(self),
430        };
431
432        let transaction = transaction.prepopulated()?;
433        let signer_key = self
434            .signer
435            .get_public_key()
436            .await
437            .map_err(SignerError::from)
438            .map_err(MetaSignError::from)?;
439        let (nonce, block_hash, block_height) = self
440            .signer
441            .fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
442            .await
443            .map_err(MetaSignError::from)?;
444        self.presign_offline(signer_key, block_hash, nonce, block_height)
445            .await
446    }
447
448    /// Signs the transaction with the default mainnet configuration but doesn't broadcast it.
449    ///
450    /// Signed transaction is stored in the [Self::transaction] struct variable.
451    ///
452    /// The provided call will fetch the nonce and block hash, block height from the network.
453    pub async fn presign_with_mainnet(self) -> Result<Self, ExecuteMetaTransactionsError> {
454        let network = NetworkConfig::mainnet();
455        self.presign_with(&network).await
456    }
457
458    /// Signs the transaction with the default testnet configuration but doesn't broadcast it.
459    ///
460    /// Signed transaction is stored in the [Self::transaction] struct variable.
461    ///
462    /// The provided call will fetch the nonce and block hash, block height from the network.
463    pub async fn presign_with_testnet(self) -> Result<Self, ExecuteMetaTransactionsError> {
464        let network = NetworkConfig::testnet();
465        self.presign_with(&network).await
466    }
467
468    /// Sends the transaction to the custom provided network.
469    ///
470    /// This is useful if you want to send the transaction to a non-default network configuration (e.g, custom RPC URL, sandbox).
471    /// Please note that if the transaction is not presigned, it will be sign with the network's nonce and block hash.
472    pub async fn send_to(
473        mut self,
474        network: &NetworkConfig,
475    ) -> Result<Response, ExecuteMetaTransactionsError> {
476        let (signed, transactionable) = match &mut self.transaction {
477            TransactionableOrSigned::Transactionable(transaction) => {
478                debug!(target: META_EXECUTOR_TARGET, "Preparing unsigned meta transaction");
479                (None, transaction)
480            }
481            TransactionableOrSigned::Signed((s, transaction)) => {
482                debug!(target: META_EXECUTOR_TARGET, "Using pre-signed meta transaction");
483                (Some(s.clone()), transaction)
484            }
485        };
486
487        if signed.is_none() {
488            debug!(target: META_EXECUTOR_TARGET, "Editing meta transaction with network config");
489            transactionable.edit_with_network(network).await?;
490        } else {
491            debug!(target: META_EXECUTOR_TARGET, "Validating pre-signed meta transaction with network config");
492            transactionable.validate_with_network(network).await?;
493        }
494
495        let signed = match signed {
496            Some(s) => s,
497            None => {
498                debug!(target: META_EXECUTOR_TARGET, "Signing meta transaction");
499                self.presign_with(network)
500                    .await?
501                    .transaction
502                    .signed()
503                    .expect("Expect to have it signed")
504            }
505        };
506
507        info!(
508            target: META_EXECUTOR_TARGET,
509            "Broadcasting signed meta transaction. Signer: {:?}, Receiver: {:?}, Nonce: {}, Valid until: {}",
510            signed.delegate_action.sender_id,
511            signed.delegate_action.receiver_id,
512            signed.delegate_action.nonce,
513            signed.delegate_action.max_block_height
514        );
515
516        Self::send_impl(network, signed).await
517    }
518
519    /// Sends the transaction to the default mainnet configuration.
520    ///
521    /// Please note that this will sign the transaction with the mainnet's nonce and block hash if it's not presigned yet.
522    pub async fn send_to_mainnet(self) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
523        let network = NetworkConfig::mainnet();
524        self.send_to(&network).await
525    }
526
527    /// Sends the transaction to the default testnet configuration.
528    ///
529    /// Please note that this will sign the transaction with the testnet's nonce and block hash if it's not presigned yet.
530    pub async fn send_to_testnet(self) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
531        let network = NetworkConfig::testnet();
532        self.send_to(&network).await
533    }
534
535    async fn send_impl(
536        network: &NetworkConfig,
537        transaction: SignedDelegateAction,
538    ) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
539        let client = reqwest::Client::new();
540        let json_payload = serde_json::json!({
541            "signed_delegate_action": SignedDelegateActionAsBase64::from(
542                transaction.clone()
543            ).to_string(),
544        });
545        debug!(
546            target: META_EXECUTOR_TARGET,
547            "Sending meta transaction to relayer. Payload: {:?}",
548            json_payload
549        );
550        let resp = client
551            .post(
552                network
553                    .meta_transaction_relayer_url
554                    .clone()
555                    .ok_or(ExecuteMetaTransactionsError::RelayerIsNotDefined)?,
556            )
557            .json(&json_payload)
558            .send()
559            .await?;
560
561        info!(
562            target: META_EXECUTOR_TARGET,
563            "Meta transaction sent to relayer. Status: {}, Signer: {:?}, Receiver: {:?}",
564            resp.status(),
565            transaction.delegate_action.sender_id,
566            transaction.delegate_action.receiver_id
567        );
568        Ok(resp)
569    }
570}
571
572impl From<ErrorWrapperForRpcTransactionError> for SendRequestError<RpcTransactionError> {
573    fn from(err: ErrorWrapperForRpcTransactionError) -> Self {
574        match err {
575            ErrorWrapperForRpcTransactionError::InternalError(internal_error) => {
576                Self::InternalError(internal_error)
577            }
578            ErrorWrapperForRpcTransactionError::RequestValidationError(
579                rpc_request_validation_error_kind,
580            ) => Self::RequestValidationError(rpc_request_validation_error_kind),
581            ErrorWrapperForRpcTransactionError::HandlerError(server_error) => {
582                Self::ServerError(server_error)
583            }
584        }
585    }
586}