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