1use std::fmt;
2use std::sync::Arc;
3
4use near_openapi_client::types::{
5 ErrorWrapperForRpcTransactionError, FinalExecutionOutcomeView, JsonRpcRequestForSendTx,
6 JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError, RpcSendTransactionRequest,
7 RpcTransactionError, RpcTransactionResponse,
8};
9
10use near_api_types::{
11 BlockHeight, CryptoHash, Nonce, PublicKey, TxExecutionStatus,
12 transaction::{
13 PrepopulateTransaction, SignedTransaction,
14 delegate_action::{SignedDelegateAction, SignedDelegateActionAsBase64},
15 result::{ExecutionFinalResult, TransactionResult},
16 },
17};
18use reqwest::Response;
19use tracing::{debug, info};
20
21use crate::{
22 common::utils::{is_critical_transaction_error, to_retry_error},
23 config::{NetworkConfig, RetryResponse, retry},
24 errors::{
25 ArgumentValidationError, ExecuteMetaTransactionsError, ExecuteTransactionError,
26 MetaSignError, SendRequestError, SignerError, ValidationError,
27 },
28 signer::Signer,
29};
30
31use super::META_TRANSACTION_VALID_FOR_DEFAULT;
32
33const TX_EXECUTOR_TARGET: &str = "near_api::tx::executor";
34const META_EXECUTOR_TARGET: &str = "near_api::meta::executor";
35
36enum SendImplResponse {
38 Full(Box<RpcTransactionResponse>),
39 Pending(TxExecutionStatus),
40}
41
42impl fmt::Debug for SendImplResponse {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::Full(_) => write!(f, "Full(...)"),
46 Self::Pending(status) => write!(f, "Pending({status:?})"),
47 }
48 }
49}
50
51#[derive(serde::Deserialize)]
56struct MinimalTransactionResponse {
57 result: MinimalTransactionResult,
58}
59
60#[derive(serde::Deserialize)]
61struct MinimalTransactionResult {
62 final_execution_status: TxExecutionStatus,
63}
64
65#[async_trait::async_trait]
66pub trait Transactionable: Send + Sync {
67 fn prepopulated(&self) -> Result<PrepopulateTransaction, ArgumentValidationError>;
68
69 async fn validate_with_network(&self, network: &NetworkConfig) -> Result<(), ValidationError>;
71
72 async fn edit_with_network(&mut self, _network: &NetworkConfig) -> Result<(), ValidationError> {
77 Ok(())
78 }
79}
80
81pub enum TransactionableOrSigned<Signed> {
82 Transactionable(Box<dyn Transactionable + 'static>),
84 Signed((Signed, Box<dyn Transactionable + 'static>)),
86}
87
88impl<Signed> TransactionableOrSigned<Signed> {
89 pub fn signed(self) -> Option<Signed> {
90 match self {
91 Self::Signed((signed, _)) => Some(signed),
92 Self::Transactionable(_) => None,
93 }
94 }
95}
96
97impl<S> TransactionableOrSigned<S> {
98 pub fn transactionable(self) -> Box<dyn Transactionable> {
99 match self {
100 Self::Transactionable(transaction) => transaction,
101 Self::Signed((_, transaction)) => transaction,
102 }
103 }
104}
105
106pub struct ExecuteSignedTransaction {
110 pub transaction: TransactionableOrSigned<SignedTransaction>,
112 pub signer: Arc<Signer>,
114
115 pub wait_until: TxExecutionStatus,
116}
117
118impl ExecuteSignedTransaction {
119 pub fn new<T: Transactionable + 'static>(transaction: T, signer: Arc<Signer>) -> Self {
120 Self {
121 transaction: TransactionableOrSigned::Transactionable(Box::new(transaction)),
122 signer,
123 wait_until: TxExecutionStatus::Final,
124 }
125 }
126
127 pub fn meta(self) -> ExecuteMetaTransaction {
133 ExecuteMetaTransaction::from_box(self.transaction.transactionable(), self.signer)
134 }
135
136 pub const fn wait_until(mut self, wait_until: TxExecutionStatus) -> Self {
137 self.wait_until = wait_until;
138 self
139 }
140
141 pub async fn presign_offline(
148 mut self,
149 public_key: PublicKey,
150 block_hash: CryptoHash,
151 nonce: Nonce,
152 ) -> Result<Self, ExecuteTransactionError> {
153 let transaction = match &self.transaction {
154 TransactionableOrSigned::Transactionable(transaction) => transaction,
155 TransactionableOrSigned::Signed(_) => return Ok(self),
156 };
157
158 let transaction = transaction.prepopulated()?;
159
160 let signed_tr = self
161 .signer
162 .sign(transaction, public_key, nonce, block_hash)
163 .await?;
164
165 self.transaction =
166 TransactionableOrSigned::Signed((signed_tr, self.transaction.transactionable()));
167 Ok(self)
168 }
169
170 pub async fn presign_with(
177 self,
178 network: &NetworkConfig,
179 ) -> Result<Self, ExecuteTransactionError> {
180 let transaction = match &self.transaction {
181 TransactionableOrSigned::Transactionable(transaction) => transaction,
182 TransactionableOrSigned::Signed(_) => return Ok(self),
183 };
184
185 let transaction = transaction.prepopulated()?;
186
187 let signer_key = self
188 .signer
189 .get_public_key()
190 .await
191 .map_err(SignerError::from)?;
192 let (nonce, hash, _) = self
193 .signer
194 .fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
195 .await
196 .map_err(MetaSignError::from)?;
197 self.presign_offline(signer_key, hash, nonce).await
198 }
199
200 pub async fn presign_with_mainnet(self) -> Result<Self, ExecuteTransactionError> {
206 let network = NetworkConfig::mainnet();
207 self.presign_with(&network).await
208 }
209
210 pub async fn presign_with_testnet(self) -> Result<Self, ExecuteTransactionError> {
216 let network = NetworkConfig::testnet();
217 self.presign_with(&network).await
218 }
219
220 pub async fn send_to(
229 mut self,
230 network: &NetworkConfig,
231 ) -> Result<TransactionResult, ExecuteTransactionError> {
232 let (signed, transactionable) = match &mut self.transaction {
233 TransactionableOrSigned::Transactionable(transaction) => {
234 debug!(target: TX_EXECUTOR_TARGET, "Preparing unsigned transaction");
235 (None, transaction)
236 }
237 TransactionableOrSigned::Signed((s, transaction)) => {
238 debug!(target: TX_EXECUTOR_TARGET, "Using pre-signed transaction");
239 (Some(s.clone()), transaction)
240 }
241 };
242
243 let wait_until = self.wait_until;
244
245 if signed.is_none() {
246 debug!(target: TX_EXECUTOR_TARGET, "Editing transaction with network config");
247 transactionable.edit_with_network(network).await?;
248 } else {
249 debug!(target: TX_EXECUTOR_TARGET, "Validating pre-signed transaction with network config");
250 transactionable.validate_with_network(network).await?;
251 }
252
253 let signed = match signed {
254 Some(s) => s,
255 None => {
256 debug!(target: TX_EXECUTOR_TARGET, "Signing transaction");
257 self.presign_with(network)
258 .await?
259 .transaction
260 .signed()
261 .expect("Expect to have it signed")
262 }
263 };
264
265 info!(
266 target: TX_EXECUTOR_TARGET,
267 "Broadcasting signed transaction. Hash: {:?}, Signer: {:?}, Receiver: {:?}, Nonce: {}",
268 signed.get_hash(),
269 signed.transaction.signer_id(),
270 signed.transaction.receiver_id(),
271 signed.transaction.nonce(),
272 );
273
274 Self::send_impl(network, signed, wait_until).await
275 }
276
277 pub async fn send_to_mainnet(self) -> Result<TransactionResult, ExecuteTransactionError> {
281 let network = NetworkConfig::mainnet();
282 self.send_to(&network).await
283 }
284
285 pub async fn send_to_testnet(self) -> Result<TransactionResult, ExecuteTransactionError> {
289 let network = NetworkConfig::testnet();
290 self.send_to(&network).await
291 }
292
293 async fn send_impl(
294 network: &NetworkConfig,
295 signed_tr: SignedTransaction,
296 wait_until: TxExecutionStatus,
297 ) -> Result<TransactionResult, ExecuteTransactionError> {
298 let hash = signed_tr.get_hash();
299 let signed_tx_base64: near_openapi_client::types::SignedTransaction = signed_tr.into();
300 let result = retry(network.clone(), |client| {
301 let signed_tx_base64 = signed_tx_base64.clone();
302 async move {
303 let result = match client
304 .send_tx(&JsonRpcRequestForSendTx {
305 id: "0".to_string(),
306 jsonrpc: "2.0".to_string(),
307 method: near_openapi_client::types::JsonRpcRequestForSendTxMethod::SendTx,
308 params: RpcSendTransactionRequest {
309 signed_tx_base64,
310 wait_until,
311 },
312 })
313 .await
314 .map(|r| r.into_inner())
315 .map_err(SendRequestError::from)
316 {
317 Ok(
318 JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError::Variant0 {
319 result,
320 ..
321 },
322 ) => RetryResponse::Ok(SendImplResponse::Full(Box::new(result))),
323 Ok(
324 JsonRpcResponseForRpcTransactionResponseAndRpcTransactionError::Variant1 {
325 error,
326 ..
327 },
328 ) => {
329 let error: SendRequestError<RpcTransactionError> =
330 SendRequestError::from(error);
331 to_retry_error(error, is_critical_transaction_error)
332 }
333 Err(err) => {
334 if matches!(
344 wait_until,
345 TxExecutionStatus::None | TxExecutionStatus::Included
346 ) {
347 if let SendRequestError::TransportError(
348 near_openapi_client::Error::InvalidResponsePayload(ref bytes, _),
349 ) = err
350 {
351 if let Ok(minimal) =
352 serde_json::from_slice::<MinimalTransactionResponse>(bytes)
353 {
354 return RetryResponse::Ok(SendImplResponse::Pending(
355 minimal.result.final_execution_status,
356 ));
357 }
358 }
359 }
360 to_retry_error(err, is_critical_transaction_error)
361 }
362 };
363
364 tracing::debug!(
365 target: TX_EXECUTOR_TARGET,
366 "Broadcasting transaction {} resulted in {:?}",
367 hash,
368 result
369 );
370
371 result
372 }
373 })
374 .await
375 .map_err(ExecuteTransactionError::TransactionError)?;
376
377 match result {
378 SendImplResponse::Pending(status) => Ok(TransactionResult::Pending { status }),
379 SendImplResponse::Full(rpc_response) => {
380 let final_execution_outcome_view = match *rpc_response {
381 RpcTransactionResponse::Variant0 {
383 final_execution_status: _,
384 receipts: _,
385 receipts_outcome,
386 status,
387 transaction,
388 transaction_outcome,
389 } => FinalExecutionOutcomeView {
390 receipts_outcome,
391 status,
392 transaction,
393 transaction_outcome,
394 },
395 RpcTransactionResponse::Variant1 {
396 final_execution_status: _,
397 receipts_outcome,
398 status,
399 transaction,
400 transaction_outcome,
401 } => FinalExecutionOutcomeView {
402 receipts_outcome,
403 status,
404 transaction,
405 transaction_outcome,
406 },
407 };
408
409 Ok(TransactionResult::Full(Box::new(
410 ExecutionFinalResult::try_from(final_execution_outcome_view)?,
411 )))
412 }
413 }
414 }
415}
416
417pub struct ExecuteMetaTransaction {
418 pub transaction: TransactionableOrSigned<SignedDelegateAction>,
419 pub signer: Arc<Signer>,
420 pub tx_live_for: Option<BlockHeight>,
421}
422
423impl ExecuteMetaTransaction {
424 pub fn new<T: Transactionable + 'static>(transaction: T, signer: Arc<Signer>) -> Self {
425 Self {
426 transaction: TransactionableOrSigned::Transactionable(Box::new(transaction)),
427 signer,
428 tx_live_for: None,
429 }
430 }
431
432 pub fn from_box(transaction: Box<dyn Transactionable + 'static>, signer: Arc<Signer>) -> Self {
433 Self {
434 transaction: TransactionableOrSigned::Transactionable(transaction),
435 signer,
436 tx_live_for: None,
437 }
438 }
439
440 pub const fn tx_live_for(mut self, tx_live_for: BlockHeight) -> Self {
445 self.tx_live_for = Some(tx_live_for);
446 self
447 }
448
449 pub async fn presign_offline(
457 mut self,
458 signer_key: PublicKey,
459 block_hash: CryptoHash,
460 nonce: Nonce,
461 block_height: BlockHeight,
462 ) -> Result<Self, ExecuteMetaTransactionsError> {
463 let transaction = match &self.transaction {
464 TransactionableOrSigned::Transactionable(transaction) => transaction,
465 TransactionableOrSigned::Signed(_) => return Ok(self),
466 };
467
468 let transaction = transaction.prepopulated()?;
469 let max_block_height = block_height
470 + self
471 .tx_live_for
472 .unwrap_or(META_TRANSACTION_VALID_FOR_DEFAULT);
473
474 let signed_tr = self
475 .signer
476 .sign_meta(transaction, signer_key, nonce, block_hash, max_block_height)
477 .await?;
478
479 self.transaction =
480 TransactionableOrSigned::Signed((signed_tr, self.transaction.transactionable()));
481 Ok(self)
482 }
483
484 pub async fn presign_with(
490 self,
491 network: &NetworkConfig,
492 ) -> Result<Self, ExecuteMetaTransactionsError> {
493 let transaction = match &self.transaction {
494 TransactionableOrSigned::Transactionable(transaction) => transaction,
495 TransactionableOrSigned::Signed(_) => return Ok(self),
496 };
497
498 let transaction = transaction.prepopulated()?;
499 let signer_key = self
500 .signer
501 .get_public_key()
502 .await
503 .map_err(SignerError::from)
504 .map_err(MetaSignError::from)?;
505 let (nonce, block_hash, block_height) = self
506 .signer
507 .fetch_tx_nonce(transaction.signer_id.clone(), signer_key, network)
508 .await
509 .map_err(MetaSignError::from)?;
510 self.presign_offline(signer_key, block_hash, nonce, block_height)
511 .await
512 }
513
514 pub async fn presign_with_mainnet(self) -> Result<Self, ExecuteMetaTransactionsError> {
520 let network = NetworkConfig::mainnet();
521 self.presign_with(&network).await
522 }
523
524 pub async fn presign_with_testnet(self) -> Result<Self, ExecuteMetaTransactionsError> {
530 let network = NetworkConfig::testnet();
531 self.presign_with(&network).await
532 }
533
534 pub async fn send_to(
539 mut self,
540 network: &NetworkConfig,
541 ) -> Result<Response, ExecuteMetaTransactionsError> {
542 let (signed, transactionable) = match &mut self.transaction {
543 TransactionableOrSigned::Transactionable(transaction) => {
544 debug!(target: META_EXECUTOR_TARGET, "Preparing unsigned meta transaction");
545 (None, transaction)
546 }
547 TransactionableOrSigned::Signed((s, transaction)) => {
548 debug!(target: META_EXECUTOR_TARGET, "Using pre-signed meta transaction");
549 (Some(s.clone()), transaction)
550 }
551 };
552
553 if signed.is_none() {
554 debug!(target: META_EXECUTOR_TARGET, "Editing meta transaction with network config");
555 transactionable.edit_with_network(network).await?;
556 } else {
557 debug!(target: META_EXECUTOR_TARGET, "Validating pre-signed meta transaction with network config");
558 transactionable.validate_with_network(network).await?;
559 }
560
561 let signed = match signed {
562 Some(s) => s,
563 None => {
564 debug!(target: META_EXECUTOR_TARGET, "Signing meta transaction");
565 self.presign_with(network)
566 .await?
567 .transaction
568 .signed()
569 .expect("Expect to have it signed")
570 }
571 };
572
573 info!(
574 target: META_EXECUTOR_TARGET,
575 "Broadcasting signed meta transaction. Signer: {:?}, Receiver: {:?}, Nonce: {}, Valid until: {}",
576 signed.delegate_action.sender_id,
577 signed.delegate_action.receiver_id,
578 signed.delegate_action.nonce,
579 signed.delegate_action.max_block_height
580 );
581
582 Self::send_impl(network, signed).await
583 }
584
585 pub async fn send_to_mainnet(self) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
589 let network = NetworkConfig::mainnet();
590 self.send_to(&network).await
591 }
592
593 pub async fn send_to_testnet(self) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
597 let network = NetworkConfig::testnet();
598 self.send_to(&network).await
599 }
600
601 async fn send_impl(
602 network: &NetworkConfig,
603 transaction: SignedDelegateAction,
604 ) -> Result<reqwest::Response, ExecuteMetaTransactionsError> {
605 let client = reqwest::Client::new();
606 let json_payload = serde_json::json!({
607 "signed_delegate_action": SignedDelegateActionAsBase64::from(
608 transaction.clone()
609 ).to_string(),
610 });
611 debug!(
612 target: META_EXECUTOR_TARGET,
613 "Sending meta transaction to relayer. Payload: {:?}",
614 json_payload
615 );
616 let resp = client
617 .post(
618 network
619 .meta_transaction_relayer_url
620 .clone()
621 .ok_or(ExecuteMetaTransactionsError::RelayerIsNotDefined)?,
622 )
623 .json(&json_payload)
624 .send()
625 .await?;
626
627 info!(
628 target: META_EXECUTOR_TARGET,
629 "Meta transaction sent to relayer. Status: {}, Signer: {:?}, Receiver: {:?}",
630 resp.status(),
631 transaction.delegate_action.sender_id,
632 transaction.delegate_action.receiver_id
633 );
634 Ok(resp)
635 }
636}
637
638impl From<ErrorWrapperForRpcTransactionError> for SendRequestError<RpcTransactionError> {
639 fn from(err: ErrorWrapperForRpcTransactionError) -> Self {
640 match err {
641 ErrorWrapperForRpcTransactionError::InternalError(internal_error) => {
642 Self::InternalError(internal_error)
643 }
644 ErrorWrapperForRpcTransactionError::RequestValidationError(
645 rpc_request_validation_error_kind,
646 ) => Self::RequestValidationError(rpc_request_validation_error_kind),
647 ErrorWrapperForRpcTransactionError::HandlerError(server_error) => {
648 Self::ServerError(server_error)
649 }
650 }
651 }
652}