Skip to main content

near_api_types/transaction/
mod.rs

1use std::{io::Write, str::FromStr, sync::OnceLock};
2
3pub mod actions;
4pub mod delegate_action;
5pub mod result;
6
7use base64::{Engine, prelude::BASE64_STANDARD};
8use borsh::{BorshDeserialize, BorshSerialize};
9use serde::{Deserialize, Serialize};
10
11use crate::{
12    AccountId, Action, CryptoHash, Nonce, PublicKey, Signature, errors::DataConversionError,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
16pub struct TransactionV0 {
17    pub signer_id: AccountId,
18    pub public_key: PublicKey,
19    pub nonce: Nonce,
20    pub receiver_id: AccountId,
21    pub block_hash: CryptoHash,
22    pub actions: Vec<Action>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
26pub struct TransactionV1 {
27    pub signer_id: AccountId,
28    pub public_key: PublicKey,
29    pub nonce: Nonce,
30    pub receiver_id: AccountId,
31    pub block_hash: CryptoHash,
32    pub actions: Vec<Action>,
33    pub priority_fee: u64,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
37pub enum Transaction {
38    V0(TransactionV0),
39    V1(TransactionV1),
40}
41
42impl Transaction {
43    pub const fn signer_id(&self) -> &AccountId {
44        match self {
45            Self::V0(tx) => &tx.signer_id,
46            Self::V1(tx) => &tx.signer_id,
47        }
48    }
49
50    pub const fn receiver_id(&self) -> &AccountId {
51        match self {
52            Self::V0(tx) => &tx.receiver_id,
53            Self::V1(tx) => &tx.receiver_id,
54        }
55    }
56
57    pub const fn nonce(&self) -> Nonce {
58        match self {
59            Self::V0(tx) => tx.nonce,
60            Self::V1(tx) => tx.nonce,
61        }
62    }
63
64    pub const fn public_key(&self) -> PublicKey {
65        match self {
66            Self::V0(tx) => tx.public_key,
67            Self::V1(tx) => tx.public_key,
68        }
69    }
70
71    pub fn actions(&self) -> &[Action] {
72        match self {
73            Self::V0(tx) => &tx.actions,
74            Self::V1(tx) => &tx.actions,
75        }
76    }
77
78    pub const fn actions_mut(&mut self) -> &mut Vec<Action> {
79        match self {
80            Self::V0(tx) => &mut tx.actions,
81            Self::V1(tx) => &mut tx.actions,
82        }
83    }
84
85    pub fn take_actions(&mut self) -> Vec<Action> {
86        let actions = match self {
87            Self::V0(tx) => &mut tx.actions,
88            Self::V1(tx) => &mut tx.actions,
89        };
90        std::mem::take(actions)
91    }
92
93    pub fn get_hash(&self) -> CryptoHash {
94        #[allow(clippy::expect_used)]
95        let bytes = borsh::to_vec(&self).expect("Failed to serialize");
96        CryptoHash::hash(&bytes)
97    }
98}
99
100impl BorshSerialize for Transaction {
101    fn serialize<W: Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
102        match self {
103            Self::V0(tx) => BorshSerialize::serialize(tx, writer)?,
104            Self::V1(tx) => {
105                BorshSerialize::serialize(&1_u8, writer)?;
106                BorshSerialize::serialize(tx, writer)?;
107            }
108        }
109        Ok(())
110    }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
114pub struct SignedTransaction {
115    pub transaction: Transaction,
116    pub signature: Signature,
117    #[borsh(skip)]
118    #[serde(skip)]
119    hash: OnceLock<CryptoHash>,
120}
121
122impl TryFrom<near_openapi_types::SignedTransactionView> for SignedTransaction {
123    type Error = DataConversionError;
124
125    fn try_from(value: near_openapi_types::SignedTransactionView) -> Result<Self, Self::Error> {
126        let near_openapi_types::SignedTransactionView {
127            signer_id,
128            public_key,
129            nonce,
130            receiver_id,
131            actions,
132            priority_fee,
133            hash,
134            signature,
135        } = value;
136
137        let transaction = if priority_fee > 0 {
138            Transaction::V1(TransactionV1 {
139                signer_id,
140                public_key: public_key.try_into()?,
141                nonce,
142                receiver_id,
143                block_hash: hash.into(),
144                actions: actions
145                    .into_iter()
146                    .map(Action::try_from)
147                    .collect::<Result<Vec<_>, _>>()?,
148                priority_fee,
149            })
150        } else {
151            Transaction::V0(TransactionV0 {
152                signer_id,
153                public_key: public_key.try_into()?,
154                nonce,
155                receiver_id,
156                block_hash: hash.into(),
157                actions: actions
158                    .into_iter()
159                    .map(Action::try_from)
160                    .collect::<Result<Vec<_>, _>>()?,
161            })
162        };
163
164        Ok(Self::new(Signature::from_str(&signature)?, transaction))
165    }
166}
167
168impl From<SignedTransaction> for near_openapi_types::SignedTransaction {
169    fn from(transaction: SignedTransaction) -> Self {
170        #[allow(clippy::expect_used)]
171        let bytes = borsh::to_vec(&transaction).expect("Failed to serialize");
172        Self(BASE64_STANDARD.encode(bytes))
173    }
174}
175
176impl From<SignedTransaction> for PrepopulateTransaction {
177    fn from(mut transaction: SignedTransaction) -> Self {
178        Self {
179            signer_id: transaction.transaction.signer_id().clone(),
180            receiver_id: transaction.transaction.receiver_id().clone(),
181            actions: transaction.transaction.take_actions(),
182        }
183    }
184}
185
186impl SignedTransaction {
187    pub const fn new(signature: Signature, transaction: Transaction) -> Self {
188        Self {
189            signature,
190            transaction,
191            hash: OnceLock::new(),
192        }
193    }
194
195    pub fn get_hash(&self) -> CryptoHash {
196        *self.hash.get_or_init(|| self.transaction.get_hash())
197    }
198}
199
200/// An internal type that represents unsigned transaction.
201#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
202pub struct PrepopulateTransaction {
203    /// The account that will sign the transaction.
204    pub signer_id: near_account_id::AccountId,
205    /// The account that will receive the transaction
206    pub receiver_id: near_account_id::AccountId,
207    /// The actions that will be executed by the transaction.
208    pub actions: Vec<Action>,
209}