near_api/
transactions.rs

1use std::sync::Arc;
2
3use near_api_types::{transaction::PrepopulateTransaction, AccountId, Action};
4
5use crate::{
6    common::send::{ExecuteSignedTransaction, Transactionable},
7    config::NetworkConfig,
8    errors::{ArgumentValidationError, ValidationError},
9    signer::Signer,
10};
11
12#[derive(Clone, Debug)]
13pub struct TransactionWithSign<T: Transactionable + 'static> {
14    pub tx: T,
15}
16
17impl<T: Transactionable> TransactionWithSign<T> {
18    pub fn with_signer(self, signer: Arc<Signer>) -> ExecuteSignedTransaction {
19        ExecuteSignedTransaction::new(self.tx, signer)
20    }
21}
22
23#[derive(Clone, Debug)]
24pub struct SelfActionBuilder {
25    pub actions: Vec<Action>,
26}
27
28impl Default for SelfActionBuilder {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl SelfActionBuilder {
35    pub const fn new() -> Self {
36        Self {
37            actions: Vec::new(),
38        }
39    }
40
41    /// Adds an action to the transaction.
42    pub fn add_action(mut self, action: Action) -> Self {
43        self.actions.push(action);
44        self
45    }
46
47    /// Adds multiple actions to the transaction.
48    pub fn add_actions(mut self, actions: Vec<Action>) -> Self {
49        self.actions.extend(actions);
50        self
51    }
52
53    /// Signs the transaction with the given account id and signer related to it.
54    pub fn with_signer(
55        self,
56        signer_account_id: AccountId,
57        signer: Arc<Signer>,
58    ) -> ExecuteSignedTransaction {
59        ConstructTransaction::new(signer_account_id.clone(), signer_account_id)
60            .add_actions(self.actions)
61            .with_signer(signer)
62    }
63}
64
65/// A builder for constructing transactions using Actions.
66#[derive(Debug, Clone)]
67pub struct ConstructTransaction {
68    pub transaction: Result<PrepopulateTransaction, ArgumentValidationError>,
69}
70
71impl ConstructTransaction {
72    /// Pre-populates a transaction with the given signer and receiver IDs.
73    pub const fn new(signer_id: AccountId, receiver_id: AccountId) -> Self {
74        Self {
75            transaction: Ok(PrepopulateTransaction {
76                signer_id,
77                receiver_id,
78                actions: Vec::new(),
79            }),
80        }
81    }
82
83    pub fn with_deferred_error(mut self, error: ArgumentValidationError) -> Self {
84        self.transaction = Err(error);
85        self
86    }
87
88    /// Adds an action to the transaction.
89    pub fn add_action(mut self, action: Action) -> Self {
90        if let Ok(transaction) = &mut self.transaction {
91            transaction.actions.push(action);
92        }
93        self
94    }
95
96    /// Adds multiple actions to the transaction.
97    pub fn add_actions(mut self, actions: Vec<Action>) -> Self {
98        if let Ok(transaction) = &mut self.transaction {
99            transaction.actions.extend(actions);
100        }
101        self
102    }
103
104    /// Signs the transaction with the given signer.
105    pub fn with_signer(self, signer: Arc<Signer>) -> ExecuteSignedTransaction {
106        ExecuteSignedTransaction::new(self, signer)
107    }
108}
109
110#[async_trait::async_trait]
111impl Transactionable for ConstructTransaction {
112    fn prepopulated(&self) -> Result<PrepopulateTransaction, ArgumentValidationError> {
113        self.transaction.clone()
114    }
115
116    async fn validate_with_network(&self, _: &NetworkConfig) -> Result<(), ValidationError> {
117        if let Err(e) = &self.transaction {
118            return Err(e.to_owned().into());
119        }
120        Ok(())
121    }
122}
123
124/// Transaction related functionality.
125///
126/// This struct provides ability to interact with transactions.
127#[derive(Clone, Debug)]
128pub struct Transaction;
129
130impl Transaction {
131    /// Constructs a new transaction builder with the given signer and receiver IDs.
132    /// This pattern is useful for batching actions into a single transaction.
133    ///
134    /// This is the low level interface for constructing transactions.
135    /// It is designed to be used in scenarios where more control over the transaction process is required.
136    ///
137    /// # Example
138    ///
139    /// This example constructs a transaction with a two transfer actions.
140    ///
141    /// ```rust,no_run
142    /// use near_api::{*, types::{transaction::actions::{Action, TransferAction}, json::U128}};
143    ///
144    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
145    /// let signer = Signer::from_ledger()?;
146    ///
147    /// let transaction_result = Transaction::construct(
148    ///     "sender.near".parse()?,
149    ///     "receiver.near".parse()?
150    /// )
151    /// .add_action(Action::Transfer(
152    ///     TransferAction {
153    ///         deposit: NearToken::from_near(1),
154    ///     },
155    /// ))
156    /// .add_action(Action::Transfer(
157    ///     TransferAction {
158    ///         deposit: NearToken::from_near(1),
159    ///     },
160    /// ))
161    /// .with_signer(signer)
162    /// .send_to_mainnet()
163    /// .await?;
164    /// # Ok(())
165    /// # }
166    /// ```
167    pub const fn construct(signer_id: AccountId, receiver_id: AccountId) -> ConstructTransaction {
168        ConstructTransaction::new(signer_id, receiver_id)
169    }
170
171    /// Signs a transaction with the given signer.
172    ///
173    /// This provides ability to sign custom constructed pre-populated transactions.
174    ///
175    /// # Examples
176    ///
177    /// ```rust,no_run
178    /// use near_api::*;
179    ///
180    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
181    /// let signer = Signer::from_ledger()?;
182    /// # let unsigned_tx = todo!();
183    ///
184    /// let transaction_result = Transaction::use_transaction(
185    ///     unsigned_tx,
186    ///     signer
187    /// )
188    /// .send_to_mainnet()
189    /// .await?;
190    /// # Ok(())
191    /// # }
192    /// ```
193    pub fn use_transaction(
194        unsigned_tx: PrepopulateTransaction,
195        signer: Arc<Signer>,
196    ) -> ExecuteSignedTransaction {
197        ConstructTransaction::new(unsigned_tx.signer_id, unsigned_tx.receiver_id)
198            .add_actions(unsigned_tx.actions)
199            .with_signer(signer)
200    }
201
202    // TODO: fetch transaction status
203    // TODO: fetch transaction receipt
204    // TODO: fetch transaction proof
205}