Skip to main content

near_api/
transactions.rs

1use std::sync::Arc;
2
3use near_api_types::{
4    AccountId, Action, CryptoHash, TxExecutionStatus, transaction::PrepopulateTransaction,
5};
6
7use crate::{
8    common::{
9        query::{
10            ReceiptHandler, RequestBuilder, TransactionStatusHandler,
11            tx_rpc::{
12                ReceiptRef, ReceiptRpc, TransactionProofRef, TransactionProofRpc,
13                TransactionStatusRef, TransactionStatusRpc,
14            },
15        },
16        send::{ExecuteSignedTransaction, Transactionable},
17    },
18    config::NetworkConfig,
19    errors::{ArgumentValidationError, ValidationError},
20    signer::Signer,
21};
22
23#[derive(Clone, Debug)]
24pub struct TransactionWithSign<T: Transactionable + 'static> {
25    pub tx: T,
26}
27
28impl<T: Transactionable> TransactionWithSign<T> {
29    pub fn with_signer(self, signer: Arc<Signer>) -> ExecuteSignedTransaction {
30        ExecuteSignedTransaction::new(self.tx, signer)
31    }
32}
33
34#[derive(Clone, Debug)]
35pub struct SelfActionBuilder {
36    pub actions: Vec<Action>,
37}
38
39impl Default for SelfActionBuilder {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44
45impl SelfActionBuilder {
46    pub const fn new() -> Self {
47        Self {
48            actions: Vec::new(),
49        }
50    }
51
52    /// Adds an action to the transaction.
53    pub fn add_action(mut self, action: Action) -> Self {
54        self.actions.push(action);
55        self
56    }
57
58    /// Adds multiple actions to the transaction.
59    pub fn add_actions(mut self, actions: Vec<Action>) -> Self {
60        self.actions.extend(actions);
61        self
62    }
63
64    /// Signs the transaction with the given account id and signer related to it.
65    pub fn with_signer(
66        self,
67        signer_account_id: AccountId,
68        signer: Arc<Signer>,
69    ) -> ExecuteSignedTransaction {
70        ConstructTransaction::new(signer_account_id.clone(), signer_account_id)
71            .add_actions(self.actions)
72            .with_signer(signer)
73    }
74}
75
76/// A builder for constructing transactions using Actions.
77#[derive(Debug, Clone)]
78pub struct ConstructTransaction {
79    pub transaction: Result<PrepopulateTransaction, ArgumentValidationError>,
80}
81
82impl ConstructTransaction {
83    /// Pre-populates a transaction with the given signer and receiver IDs.
84    pub const fn new(signer_id: AccountId, receiver_id: AccountId) -> Self {
85        Self {
86            transaction: Ok(PrepopulateTransaction {
87                signer_id,
88                receiver_id,
89                actions: Vec::new(),
90            }),
91        }
92    }
93
94    pub fn with_deferred_error(mut self, error: ArgumentValidationError) -> Self {
95        self.transaction = Err(error);
96        self
97    }
98
99    /// Adds an action to the transaction.
100    pub fn add_action(mut self, action: Action) -> Self {
101        if let Ok(transaction) = &mut self.transaction {
102            transaction.actions.push(action);
103        }
104        self
105    }
106
107    /// Adds multiple actions to the transaction.
108    pub fn add_actions(mut self, actions: Vec<Action>) -> Self {
109        if let Ok(transaction) = &mut self.transaction {
110            transaction.actions.extend(actions);
111        }
112        self
113    }
114
115    /// Signs the transaction with the given signer.
116    pub fn with_signer(self, signer: Arc<Signer>) -> ExecuteSignedTransaction {
117        ExecuteSignedTransaction::new(self, signer)
118    }
119}
120
121#[async_trait::async_trait]
122impl Transactionable for ConstructTransaction {
123    fn prepopulated(&self) -> Result<PrepopulateTransaction, ArgumentValidationError> {
124        self.transaction.clone()
125    }
126
127    async fn validate_with_network(&self, _: &NetworkConfig) -> Result<(), ValidationError> {
128        if let Err(e) = &self.transaction {
129            return Err(e.to_owned().into());
130        }
131        Ok(())
132    }
133}
134
135/// Transaction related functionality.
136///
137/// This struct provides ability to interact with transactions.
138#[derive(Clone, Debug)]
139pub struct Transaction;
140
141impl Transaction {
142    /// Constructs a new transaction builder with the given signer and receiver IDs.
143    /// This pattern is useful for batching actions into a single transaction.
144    ///
145    /// This is the low level interface for constructing transactions.
146    /// It is designed to be used in scenarios where more control over the transaction process is required.
147    ///
148    /// # Example
149    ///
150    /// This example constructs a transaction with a two transfer actions.
151    ///
152    /// ```rust,no_run
153    /// use near_api::{*, types::{transaction::actions::{Action, TransferAction}, json::U128}};
154    ///
155    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
156    /// let signer = Signer::from_ledger()?;
157    ///
158    /// let transaction_result = Transaction::construct(
159    ///     "sender.near".parse()?,
160    ///     "receiver.near".parse()?
161    /// )
162    /// .add_action(Action::Transfer(
163    ///     TransferAction {
164    ///         deposit: NearToken::from_near(1),
165    ///     },
166    /// ))
167    /// .add_action(Action::Transfer(
168    ///     TransferAction {
169    ///         deposit: NearToken::from_near(1),
170    ///     },
171    /// ))
172    /// .with_signer(signer)
173    /// .send_to_mainnet()
174    /// .await?;
175    /// # Ok(())
176    /// # }
177    /// ```
178    pub const fn construct(signer_id: AccountId, receiver_id: AccountId) -> ConstructTransaction {
179        ConstructTransaction::new(signer_id, receiver_id)
180    }
181
182    /// Signs a transaction with the given signer.
183    ///
184    /// This provides ability to sign custom constructed pre-populated transactions.
185    ///
186    /// # Examples
187    ///
188    /// ```rust,no_run
189    /// use near_api::*;
190    ///
191    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
192    /// let signer = Signer::from_ledger()?;
193    /// # let unsigned_tx = todo!();
194    ///
195    /// let transaction_result = Transaction::use_transaction(
196    ///     unsigned_tx,
197    ///     signer
198    /// )
199    /// .send_to_mainnet()
200    /// .await?;
201    /// # Ok(())
202    /// # }
203    /// ```
204    pub fn use_transaction(
205        unsigned_tx: PrepopulateTransaction,
206        signer: Arc<Signer>,
207    ) -> ExecuteSignedTransaction {
208        ConstructTransaction::new(unsigned_tx.signer_id, unsigned_tx.receiver_id)
209            .add_actions(unsigned_tx.actions)
210            .with_signer(signer)
211    }
212
213    /// Sets up a query to fetch the current status of a transaction by its hash and sender account ID.
214    ///
215    /// Waits until the transaction has been optimistically executed ([`TxExecutionStatus::ExecutedOptimistic`]),
216    /// ensuring that outcome fields (gas usage, logs, status) are populated.
217    /// If you need to wait until the transaction reaches a different stage
218    /// (e.g., [`TxExecutionStatus::Final`] or [`TxExecutionStatus::None`]),
219    /// use [`Transaction::status_with_options`] instead.
220    ///
221    /// The returned result is an [`ExecutionFinalResult`](near_api_types::transaction::result::ExecutionFinalResult)
222    /// which provides details about gas usage, logs, and the execution status.
223    ///
224    /// # Example
225    ///
226    /// ```rust,no_run
227    /// use near_api::*;
228    ///
229    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
230    /// let tx_hash: CryptoHash = "EaNakSaXUTjbPsUJbuDdbuq3e6Ynmjo8zYUgDVqt1iTn".parse()?;
231    /// let sender: AccountId = "sender.near".parse()?;
232    ///
233    /// let result = Transaction::status(sender, tx_hash)
234    ///     .fetch_from_mainnet()
235    ///     .await?;
236    /// println!("Transaction success: {}", result.is_success());
237    /// # Ok(())
238    /// # }
239    /// ```
240    pub fn status(
241        sender_account_id: AccountId,
242        tx_hash: CryptoHash,
243    ) -> RequestBuilder<TransactionStatusHandler> {
244        Self::status_with_options(
245            sender_account_id,
246            tx_hash,
247            TxExecutionStatus::ExecutedOptimistic,
248        )
249    }
250
251    /// Sets up a query to fetch the status of a transaction, waiting until it reaches
252    /// the specified execution stage.
253    ///
254    /// Use [`TxExecutionStatus::None`] to return immediately with whatever state is available,
255    /// or [`TxExecutionStatus::Final`] to wait until the transaction is fully finalized.
256    ///
257    /// # Example
258    ///
259    /// ```rust,no_run
260    /// use near_api::{*, types::TxExecutionStatus};
261    ///
262    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
263    /// let tx_hash: CryptoHash = "EaNakSaXUTjbPsUJbuDdbuq3e6Ynmjo8zYUgDVqt1iTn".parse()?;
264    /// let sender: AccountId = "sender.near".parse()?;
265    ///
266    /// let result = Transaction::status_with_options(
267    ///     sender,
268    ///     tx_hash,
269    ///     TxExecutionStatus::Final,
270    /// )
271    /// .fetch_from_mainnet()
272    /// .await?;
273    /// # Ok(())
274    /// # }
275    /// ```
276    pub fn status_with_options(
277        sender_account_id: AccountId,
278        tx_hash: CryptoHash,
279        wait_until: TxExecutionStatus,
280    ) -> RequestBuilder<TransactionStatusHandler> {
281        RequestBuilder::new(
282            TransactionStatusRpc,
283            TransactionStatusRef {
284                sender_account_id,
285                tx_hash,
286                wait_until,
287            },
288            TransactionStatusHandler,
289        )
290    }
291
292    /// Sets up a query to fetch a receipt by its ID.
293    ///
294    /// This uses the `EXPERIMENTAL_receipt` RPC method to retrieve the details of a specific receipt.
295    ///
296    /// # Example
297    ///
298    /// ```rust,no_run
299    /// use near_api::*;
300    ///
301    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
302    /// let receipt_id: CryptoHash = "EaNakSaXUTjbPsUJbuDdbuq3e6Ynmjo8zYUgDVqt1iTn".parse()?;
303    ///
304    /// let receipt = Transaction::receipt(receipt_id)
305    ///     .fetch_from_mainnet()
306    ///     .await?;
307    /// println!("Receipt receiver: {:?}", receipt.receiver_id);
308    /// # Ok(())
309    /// # }
310    /// ```
311    pub fn receipt(receipt_id: CryptoHash) -> RequestBuilder<ReceiptHandler> {
312        RequestBuilder::new(ReceiptRpc, ReceiptRef { receipt_id }, ReceiptHandler)
313    }
314
315    /// Sets up a query to fetch the light client execution proof for a transaction.
316    ///
317    /// This is used to verify a transaction's execution against a light client block header.
318    /// The `light_client_head` parameter specifies the block hash of the light client's latest known head.
319    ///
320    /// # Example
321    ///
322    /// ```rust,no_run
323    /// use near_api::*;
324    ///
325    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
326    /// let tx_hash: CryptoHash = "EaNakSaXUTjbPsUJbuDdbuq3e6Ynmjo8zYUgDVqt1iTn".parse()?;
327    /// let sender: AccountId = "sender.near".parse()?;
328    /// let head_hash: CryptoHash = "3i1SypXzBRhLMvpHmNJXpg18FgVW6jNFrFcUqBF5Wmit".parse()?;
329    ///
330    /// let proof = Transaction::proof(sender, tx_hash, head_hash)
331    ///     .fetch_from_mainnet()
332    ///     .await?;
333    /// println!("Proof block header: {:?}", proof.block_header_lite);
334    /// # Ok(())
335    /// # }
336    /// ```
337    pub fn proof(
338        sender_id: AccountId,
339        transaction_hash: CryptoHash,
340        light_client_head: CryptoHash,
341    ) -> RequestBuilder<TransactionProofRpc> {
342        RequestBuilder::new(
343            TransactionProofRpc,
344            TransactionProofRef {
345                sender_id,
346                transaction_hash,
347                light_client_head,
348            },
349            TransactionProofRpc,
350        )
351    }
352}