near_api_types/transaction/
actions.rs

1use std::collections::BTreeMap;
2use std::str::FromStr;
3
4use crate::AccountId;
5use base64::prelude::BASE64_STANDARD;
6use base64::Engine;
7use borsh::{BorshDeserialize, BorshSerialize};
8use serde::{Deserialize, Serialize};
9use serde_with::{base64::Base64, serde_as};
10
11use crate::errors::DataConversionError;
12use crate::json::U64;
13use crate::transaction::delegate_action::SignedDelegateAction;
14use crate::utils::near_gas_as_u64;
15use crate::{CryptoHash, NearGas, NearToken, PublicKey, Signature};
16
17#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
18pub enum Action {
19    /// Create an (sub)account using a transaction `receiver_id` as an ID for
20    /// a new account ID must pass validation rules described here
21    /// <http://nomicon.io/Primitives/Account.html>.
22    CreateAccount(CreateAccountAction),
23    /// Sets a Wasm code to a receiver_id
24    DeployContract(DeployContractAction),
25    /// Call a function on a contract
26    FunctionCall(Box<FunctionCallAction>),
27    /// Transfer tokens to an account
28    Transfer(TransferAction),
29    /// Stake tokens from an account to a validator
30    /// As a not a developer of staking pool, you should consider using a staking pool contract instead
31    Stake(Box<StakeAction>),
32    /// Add a key to an account
33    AddKey(Box<AddKeyAction>),
34    /// Delete a key from an account
35    DeleteKey(Box<DeleteKeyAction>),
36    /// Delete an account and transfer all tokens to a beneficiary account
37    DeleteAccount(DeleteAccountAction),
38    /// Delegate your action submission to some relayer that will cover the cost of the transaction
39    Delegate(Box<SignedDelegateAction>),
40    /// Deploy a global contract
41    DeployGlobalContract(DeployGlobalContractAction),
42    /// Use a global contract to link code to an account
43    UseGlobalContract(Box<UseGlobalContractAction>),
44    /// Deploy a deterministic account with global contract and state.
45    ///
46    /// See [NEP-616](https://github.com/near/NEPs/blob/master/neps/nep-0616.md) for more details
47    DeterministicStateInit(Box<DeterministicStateInitAction>),
48
49    /// Creates a gas key for an account to be used for gas payments
50    ///
51    /// See [NEP-611](https://github.com/near/NEPs/blob/master/neps/nep-0611.md) for more details
52    AddGasKey(Box<AddGasKeyAction>),
53    /// Deletes a gas key for an account
54    ///
55    /// See [NEP-611](https://github.com/near/NEPs/blob/master/neps/nep-0611.md) for more details
56    DeleteGasKey(Box<DeleteGasKeyAction>),
57    /// Transfers tokens to a gas key
58    ///
59    /// See [NEP-611](https://github.com/near/NEPs/blob/master/neps/nep-0611.md) for more details
60    TransferToGasKey(Box<TransferToGasKeyAction>),
61}
62
63#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
64pub struct DeterministicStateInitAction {
65    pub state_init: DeterministicAccountStateInit,
66    pub deposit: NearToken,
67}
68
69#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
70#[borsh(use_discriminant = true)]
71#[repr(u8)]
72pub enum DeterministicAccountStateInit {
73    V1(DeterministicAccountStateInitV1),
74}
75
76#[serde_as]
77#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
78pub struct DeterministicAccountStateInitV1 {
79    pub code: GlobalContractIdentifier,
80    #[serde_as(as = "BTreeMap<Base64, Base64>")]
81    pub data: BTreeMap<Vec<u8>, Vec<u8>>,
82}
83
84impl TryFrom<near_openapi_types::DeterministicStateInitAction> for DeterministicStateInitAction {
85    type Error = DataConversionError;
86    fn try_from(
87        val: near_openapi_types::DeterministicStateInitAction,
88    ) -> Result<Self, Self::Error> {
89        let near_openapi_types::DeterministicStateInitAction {
90            state_init,
91            deposit,
92        } = val;
93
94        match state_init {
95            near_openapi_types::DeterministicAccountStateInit::V1(v1) => Ok(Self {
96                state_init: DeterministicAccountStateInit::V1(DeterministicAccountStateInitV1 {
97                    code: v1.code.try_into()?,
98                    data: v1
99                        .data
100                        .into_iter()
101                        .map(|(k, v)| {
102                            Ok::<(Vec<u8>, Vec<u8>), DataConversionError>((
103                                BASE64_STANDARD.decode(k)?,
104                                BASE64_STANDARD.decode(v)?,
105                            ))
106                        })
107                        .collect::<Result<BTreeMap<Vec<u8>, Vec<u8>>, _>>()?,
108                }),
109                deposit,
110            }),
111        }
112    }
113}
114
115#[serde_as]
116#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
117pub struct DeployGlobalContractAction {
118    #[serde_as(as = "Base64")]
119    pub code: Vec<u8>,
120    pub deploy_mode: GlobalContractDeployMode,
121}
122
123impl TryFrom<near_openapi_types::DeployGlobalContractAction> for DeployGlobalContractAction {
124    type Error = DataConversionError;
125    fn try_from(val: near_openapi_types::DeployGlobalContractAction) -> Result<Self, Self::Error> {
126        let near_openapi_types::DeployGlobalContractAction { code, deploy_mode } = val;
127        Ok(Self {
128            code: BASE64_STANDARD.decode(code)?,
129            deploy_mode: deploy_mode.into(),
130        })
131    }
132}
133#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
134pub struct UseGlobalContractAction {
135    pub contract_identifier: GlobalContractIdentifier,
136}
137
138impl TryFrom<near_openapi_types::UseGlobalContractAction> for UseGlobalContractAction {
139    type Error = DataConversionError;
140    fn try_from(val: near_openapi_types::UseGlobalContractAction) -> Result<Self, Self::Error> {
141        let near_openapi_types::UseGlobalContractAction {
142            contract_identifier,
143        } = val;
144        Ok(Self {
145            contract_identifier: contract_identifier.try_into()?,
146        })
147    }
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
151pub struct CreateAccountAction {}
152
153impl From<near_openapi_types::CreateAccountAction> for CreateAccountAction {
154    fn from(_: near_openapi_types::CreateAccountAction) -> Self {
155        Self {}
156    }
157}
158
159#[serde_as]
160#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
161pub struct DeployContractAction {
162    #[serde_as(as = "Base64")]
163    pub code: Vec<u8>,
164}
165
166impl TryFrom<near_openapi_types::DeployContractAction> for DeployContractAction {
167    type Error = DataConversionError;
168    fn try_from(val: near_openapi_types::DeployContractAction) -> Result<Self, Self::Error> {
169        let near_openapi_types::DeployContractAction { code } = val;
170        Ok(Self {
171            code: BASE64_STANDARD.decode(code)?,
172        })
173    }
174}
175
176#[serde_as]
177#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
178pub struct FunctionCallAction {
179    pub method_name: String,
180    #[serde_as(as = "Base64")]
181    pub args: Vec<u8>,
182    #[serde(serialize_with = "near_gas_as_u64::serialize")]
183    pub gas: NearGas,
184    pub deposit: NearToken,
185}
186
187impl TryFrom<near_openapi_types::FunctionCallAction> for FunctionCallAction {
188    type Error = DataConversionError;
189    fn try_from(val: near_openapi_types::FunctionCallAction) -> Result<Self, Self::Error> {
190        let near_openapi_types::FunctionCallAction {
191            method_name,
192            args,
193            gas,
194            deposit,
195        } = val;
196        Ok(Self {
197            method_name,
198            args: BASE64_STANDARD.decode(args)?,
199            gas,
200            deposit,
201        })
202    }
203}
204
205#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
206pub struct TransferAction {
207    pub deposit: NearToken,
208}
209
210impl TryFrom<near_openapi_types::TransferAction> for TransferAction {
211    type Error = DataConversionError;
212    fn try_from(val: near_openapi_types::TransferAction) -> Result<Self, Self::Error> {
213        let near_openapi_types::TransferAction { deposit } = val;
214        Ok(Self { deposit })
215    }
216}
217
218#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
219pub struct StakeAction {
220    /// Amount of tokens to stake.
221    pub stake: NearToken,
222    /// Validator key which will be used to sign transactions on behalf of signer_id
223    pub public_key: PublicKey,
224}
225
226impl TryFrom<near_openapi_types::StakeAction> for StakeAction {
227    type Error = DataConversionError;
228    fn try_from(val: near_openapi_types::StakeAction) -> Result<Self, Self::Error> {
229        let near_openapi_types::StakeAction { public_key, stake } = val;
230        Ok(Self {
231            public_key: public_key.try_into()?,
232            stake,
233        })
234    }
235}
236
237#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
238pub struct AddGasKeyAction {
239    pub public_key: PublicKey,
240    pub num_nonces: u32,
241    pub permission: AccessKeyPermission,
242}
243
244impl TryFrom<near_openapi_types::AddGasKeyAction> for AddGasKeyAction {
245    type Error = DataConversionError;
246    fn try_from(val: near_openapi_types::AddGasKeyAction) -> Result<Self, Self::Error> {
247        let near_openapi_types::AddGasKeyAction {
248            public_key,
249            num_nonces,
250            permission,
251        } = val;
252        Ok(Self {
253            public_key: public_key.try_into()?,
254            num_nonces,
255            permission: permission.try_into()?,
256        })
257    }
258}
259
260#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
261pub struct DeleteGasKeyAction {
262    pub public_key: PublicKey,
263}
264
265impl TryFrom<near_openapi_types::DeleteGasKeyAction> for DeleteGasKeyAction {
266    type Error = DataConversionError;
267    fn try_from(val: near_openapi_types::DeleteGasKeyAction) -> Result<Self, Self::Error> {
268        let near_openapi_types::DeleteGasKeyAction { public_key } = val;
269        Ok(Self {
270            public_key: public_key.try_into()?,
271        })
272    }
273}
274
275#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
276pub struct TransferToGasKeyAction {
277    pub public_key: PublicKey,
278    pub deposit: NearToken,
279}
280
281impl TryFrom<near_openapi_types::TransferToGasKeyAction> for TransferToGasKeyAction {
282    type Error = DataConversionError;
283    fn try_from(val: near_openapi_types::TransferToGasKeyAction) -> Result<Self, Self::Error> {
284        let near_openapi_types::TransferToGasKeyAction {
285            public_key,
286            deposit,
287        } = val;
288        Ok(Self {
289            public_key: public_key.try_into()?,
290            deposit,
291        })
292    }
293}
294
295#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
296pub struct AddKeyAction {
297    /// A public key which will be associated with an access_key
298    pub public_key: PublicKey,
299    /// An access key with the permission
300    pub access_key: AccessKey,
301}
302
303impl TryFrom<near_openapi_types::AddKeyAction> for AddKeyAction {
304    type Error = DataConversionError;
305    fn try_from(val: near_openapi_types::AddKeyAction) -> Result<Self, Self::Error> {
306        let near_openapi_types::AddKeyAction {
307            public_key,
308            access_key,
309        } = val;
310        Ok(Self {
311            public_key: public_key.try_into()?,
312            access_key: access_key.try_into()?,
313        })
314    }
315}
316
317#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
318pub struct AccessKey {
319    /// Nonce for this access key, used for tx nonce generation. When access key is created, nonce
320    /// is set to `(block_height - 1) * 1e6` to avoid tx hash collision on access key re-creation.
321    /// See <https://github.com/near/nearcore/issues/3779> for more details.
322    pub nonce: U64,
323    /// Defines permissions for this access key.
324    pub permission: AccessKeyPermission,
325}
326
327impl TryFrom<near_openapi_types::AccessKeyView> for AccessKey {
328    type Error = DataConversionError;
329    fn try_from(val: near_openapi_types::AccessKeyView) -> Result<Self, Self::Error> {
330        let near_openapi_types::AccessKeyView { nonce, permission } = val;
331        Ok(Self {
332            nonce: U64(nonce),
333            permission: permission.try_into()?,
334        })
335    }
336}
337
338impl TryFrom<near_openapi_types::AccessKey> for AccessKey {
339    type Error = DataConversionError;
340    fn try_from(val: near_openapi_types::AccessKey) -> Result<Self, Self::Error> {
341        let near_openapi_types::AccessKey { nonce, permission } = val;
342        Ok(Self {
343            nonce: U64(nonce),
344            permission: permission.try_into()?,
345        })
346    }
347}
348
349#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
350pub enum AccessKeyPermission {
351    FunctionCall(FunctionCallPermission),
352    /// Grants full access to the account.
353    /// NOTE: It's used to replace account-level public keys.
354    FullAccess,
355}
356
357impl TryFrom<near_openapi_types::AccessKeyPermissionView> for AccessKeyPermission {
358    type Error = DataConversionError;
359    fn try_from(val: near_openapi_types::AccessKeyPermissionView) -> Result<Self, Self::Error> {
360        match val {
361            near_openapi_types::AccessKeyPermissionView::FunctionCall {
362                allowance,
363                method_names,
364                receiver_id,
365            } => Ok(Self::FunctionCall(FunctionCallPermission {
366                allowance,
367                receiver_id,
368                method_names,
369            })),
370            near_openapi_types::AccessKeyPermissionView::FullAccess => Ok(Self::FullAccess),
371        }
372    }
373}
374
375impl TryFrom<near_openapi_types::AccessKeyPermission> for AccessKeyPermission {
376    type Error = DataConversionError;
377    fn try_from(val: near_openapi_types::AccessKeyPermission) -> Result<Self, Self::Error> {
378        match val {
379            near_openapi_types::AccessKeyPermission::FunctionCall(function_call_permission) => {
380                Ok(Self::FunctionCall(function_call_permission.try_into()?))
381            }
382            near_openapi_types::AccessKeyPermission::FullAccess => Ok(Self::FullAccess),
383        }
384    }
385}
386
387#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
388pub struct FunctionCallPermission {
389    pub allowance: Option<NearToken>,
390    pub receiver_id: String,
391    pub method_names: Vec<String>,
392}
393
394impl TryFrom<near_openapi_types::FunctionCallPermission> for FunctionCallPermission {
395    type Error = DataConversionError;
396    fn try_from(val: near_openapi_types::FunctionCallPermission) -> Result<Self, Self::Error> {
397        let near_openapi_types::FunctionCallPermission {
398            allowance,
399            receiver_id,
400            method_names,
401        } = val;
402        Ok(Self {
403            allowance,
404            receiver_id,
405            method_names,
406        })
407    }
408}
409
410#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
411pub struct DeleteKeyAction {
412    /// A public key associated with the access_key to be deleted.
413    pub public_key: PublicKey,
414}
415
416impl TryFrom<near_openapi_types::DeleteKeyAction> for DeleteKeyAction {
417    type Error = DataConversionError;
418    fn try_from(val: near_openapi_types::DeleteKeyAction) -> Result<Self, Self::Error> {
419        let near_openapi_types::DeleteKeyAction { public_key } = val;
420        Ok(Self {
421            public_key: public_key.try_into()?,
422        })
423    }
424}
425
426#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
427pub struct DeleteAccountAction {
428    pub beneficiary_id: AccountId,
429}
430
431impl From<near_openapi_types::DeleteAccountAction> for DeleteAccountAction {
432    fn from(val: near_openapi_types::DeleteAccountAction) -> Self {
433        let near_openapi_types::DeleteAccountAction { beneficiary_id } = val;
434        Self { beneficiary_id }
435    }
436}
437
438#[derive(
439    BorshSerialize,
440    BorshDeserialize,
441    serde::Serialize,
442    serde::Deserialize,
443    PartialEq,
444    Eq,
445    Clone,
446    Debug,
447)]
448#[repr(u8)]
449pub enum GlobalContractDeployMode {
450    /// Contract is deployed under its code hash.
451    /// Users will be able reference it by that hash.
452    /// This effectively makes the contract immutable.
453    CodeHash,
454    /// Contract is deployed under the owner account id.
455    /// Users will be able reference it by that account id.
456    /// This allows the owner to update the contract for all its users.
457    AccountId,
458}
459
460impl From<near_openapi_types::GlobalContractDeployMode> for GlobalContractDeployMode {
461    fn from(val: near_openapi_types::GlobalContractDeployMode) -> Self {
462        match val {
463            near_openapi_types::GlobalContractDeployMode::CodeHash => Self::CodeHash,
464            near_openapi_types::GlobalContractDeployMode::AccountId => Self::AccountId,
465        }
466    }
467}
468#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
469pub enum GlobalContractIdentifier {
470    CodeHash(CryptoHash),
471    AccountId(AccountId),
472}
473
474impl TryFrom<near_openapi_types::GlobalContractIdentifier> for GlobalContractIdentifier {
475    type Error = DataConversionError;
476    fn try_from(val: near_openapi_types::GlobalContractIdentifier) -> Result<Self, Self::Error> {
477        match val {
478            near_openapi_types::GlobalContractIdentifier::CodeHash(code_hash) => {
479                Ok(Self::CodeHash(code_hash.try_into()?))
480            }
481            near_openapi_types::GlobalContractIdentifier::AccountId(account_id) => {
482                Ok(Self::AccountId(account_id))
483            }
484        }
485    }
486}
487
488impl TryFrom<near_openapi_types::GlobalContractIdentifierView> for GlobalContractIdentifier {
489    type Error = DataConversionError;
490    fn try_from(
491        val: near_openapi_types::GlobalContractIdentifierView,
492    ) -> Result<Self, Self::Error> {
493        let near_openapi_types::GlobalContractIdentifierView {
494            subtype_0: code_hash,
495            subtype_1: account_id,
496        } = val;
497        if let Some(code_hash) = code_hash {
498            Ok(Self::CodeHash(code_hash.try_into()?))
499        } else if let Some(account_id) = account_id {
500            Ok(Self::AccountId(account_id))
501        } else {
502            Err(DataConversionError::InvalidGlobalContractIdentifier)
503        }
504    }
505}
506
507impl TryFrom<near_openapi_types::ActionView> for Action {
508    type Error = DataConversionError;
509    fn try_from(val: near_openapi_types::ActionView) -> Result<Self, Self::Error> {
510        match val {
511            near_openapi_types::ActionView::DeterministicStateInit {
512                code,
513                data,
514                deposit,
515            } => Ok(Self::DeterministicStateInit(Box::new(
516                DeterministicStateInitAction {
517                    state_init: DeterministicAccountStateInit::V1(
518                        DeterministicAccountStateInitV1 {
519                            code: code.try_into()?,
520                            data: data
521                                .into_iter()
522                                .map(|(k, v)| {
523                                    Ok::<(Vec<u8>, Vec<u8>), DataConversionError>((
524                                        BASE64_STANDARD.decode(k)?,
525                                        BASE64_STANDARD.decode(v)?,
526                                    ))
527                                })
528                                .collect::<Result<BTreeMap<Vec<u8>, Vec<u8>>, _>>()?,
529                        },
530                    ),
531                    deposit,
532                },
533            ))),
534            near_openapi_types::ActionView::CreateAccount => {
535                Ok(Self::CreateAccount(CreateAccountAction {}))
536            }
537            near_openapi_types::ActionView::DeployContract { code } => {
538                Ok(Self::DeployContract(DeployContractAction {
539                    code: BASE64_STANDARD.decode(code)?,
540                }))
541            }
542            near_openapi_types::ActionView::FunctionCall {
543                method_name,
544                args,
545                gas,
546                deposit,
547            } => Ok(Self::FunctionCall(Box::new(FunctionCallAction {
548                method_name,
549                args: BASE64_STANDARD.decode(args.0)?,
550                gas,
551                deposit,
552            }))),
553            near_openapi_types::ActionView::Transfer { deposit } => {
554                Ok(Self::Transfer(TransferAction { deposit }))
555            }
556            near_openapi_types::ActionView::Stake { public_key, stake } => {
557                Ok(Self::Stake(Box::new(StakeAction {
558                    public_key: public_key.try_into()?,
559                    stake,
560                })))
561            }
562            near_openapi_types::ActionView::AddKey {
563                access_key,
564                public_key,
565            } => Ok(Self::AddKey(Box::new(AddKeyAction {
566                public_key: public_key.try_into()?,
567                access_key: access_key.try_into()?,
568            }))),
569            near_openapi_types::ActionView::DeleteKey { public_key } => {
570                Ok(Self::DeleteKey(Box::new(DeleteKeyAction {
571                    public_key: public_key.try_into()?,
572                })))
573            }
574            near_openapi_types::ActionView::DeleteAccount { beneficiary_id } => {
575                Ok(Self::DeleteAccount(DeleteAccountAction { beneficiary_id }))
576            }
577            near_openapi_types::ActionView::Delegate {
578                delegate_action,
579                signature,
580            } => Ok(Self::Delegate(Box::new(SignedDelegateAction {
581                delegate_action: delegate_action.try_into()?,
582                signature: Signature::from_str(&signature)?,
583            }))),
584            near_openapi_types::ActionView::DeployGlobalContract { code } => {
585                Ok(Self::DeployGlobalContract(DeployGlobalContractAction {
586                    code: BASE64_STANDARD.decode(code)?,
587                    deploy_mode: GlobalContractDeployMode::CodeHash,
588                }))
589            }
590            near_openapi_types::ActionView::DeployGlobalContractByAccountId { code } => {
591                Ok(Self::DeployGlobalContract(DeployGlobalContractAction {
592                    code: BASE64_STANDARD.decode(code)?,
593                    deploy_mode: GlobalContractDeployMode::AccountId,
594                }))
595            }
596            near_openapi_types::ActionView::UseGlobalContract { code_hash } => {
597                Ok(Self::UseGlobalContract(Box::new(UseGlobalContractAction {
598                    contract_identifier: GlobalContractIdentifier::CodeHash(code_hash.try_into()?),
599                })))
600            }
601            near_openapi_types::ActionView::UseGlobalContractByAccountId { account_id } => {
602                Ok(Self::UseGlobalContract(Box::new(UseGlobalContractAction {
603                    contract_identifier: GlobalContractIdentifier::AccountId(account_id),
604                })))
605            }
606            near_openapi_types::ActionView::AddGasKey {
607                num_nonces,
608                permission,
609                public_key,
610            } => Ok(Self::AddGasKey(Box::new(AddGasKeyAction {
611                public_key: public_key.try_into()?,
612                num_nonces,
613                permission: permission.try_into()?,
614            }))),
615            near_openapi_types::ActionView::DeleteGasKey { public_key } => {
616                Ok(Self::DeleteGasKey(Box::new(DeleteGasKeyAction {
617                    public_key: public_key.try_into()?,
618                })))
619            }
620            near_openapi_types::ActionView::TransferToGasKey {
621                amount: deposit,
622                public_key,
623            } => Ok(Self::TransferToGasKey(Box::new(TransferToGasKeyAction {
624                public_key: public_key.try_into()?,
625                deposit,
626            }))),
627        }
628    }
629}
630
631#[cfg(test)]
632mod tests {
633    use std::collections::BTreeMap;
634    use std::sync::Arc;
635
636    use super::*;
637    use crate::crypto::{public_key::ED25519PublicKey, ED25519_PUBLIC_KEY_LENGTH};
638    use crate::transaction::delegate_action::{DelegateAction, NonDelegateAction};
639    use near_primitives::action as npa;
640    use near_primitives::deterministic_account_id::{
641        DeterministicAccountStateInit as npaDeterministicAccountStateInit,
642        DeterministicAccountStateInitV1 as npaDeterministicAccountStateInitV1,
643    };
644    use near_primitives::gas::Gas;
645    use near_primitives::global_contract::GlobalContractIdentifier as npaGlobalContractIdentifier;
646    use serde_json;
647
648    fn get_actions() -> (Vec<Action>, Vec<npa::Action>) {
649        let btreemap = BTreeMap::from([(b"key".to_vec(), b"value".to_vec())]);
650
651        let local_actions = vec![
652            Action::CreateAccount(CreateAccountAction {}),
653            Action::DeployContract(DeployContractAction {
654                code: vec![1, 2, 3],
655            }),
656            Action::FunctionCall(Box::new(FunctionCallAction {
657                method_name: "test".to_string(),
658                args: vec![4, 5, 6],
659                gas: NearGas::from_gas(1000000),
660                deposit: NearToken::from_yoctonear(0),
661            })),
662            Action::Transfer(TransferAction {
663                deposit: NearToken::from_yoctonear(1000000000),
664            }),
665            Action::Stake(Box::new(StakeAction {
666                stake: NearToken::from_yoctonear(100000000),
667                public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
668            })),
669            Action::AddKey(Box::new(AddKeyAction {
670                public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
671                access_key: AccessKey {
672                    nonce: U64(0),
673                    permission: AccessKeyPermission::FullAccess,
674                },
675            })),
676            Action::DeleteKey(Box::new(DeleteKeyAction {
677                public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
678            })),
679            Action::DeleteAccount(DeleteAccountAction {
680                beneficiary_id: "alice.near".parse().unwrap(),
681            }),
682            Action::DeployGlobalContract(DeployGlobalContractAction {
683                code: vec![7, 8, 9],
684                deploy_mode: GlobalContractDeployMode::CodeHash,
685            }),
686            Action::UseGlobalContract(Box::new(UseGlobalContractAction {
687                contract_identifier: GlobalContractIdentifier::AccountId(
688                    "global.near".parse().unwrap(),
689                ),
690            })),
691            Action::Delegate(Box::new(SignedDelegateAction {
692                delegate_action: DelegateAction {
693                    sender_id: "sender.near".parse().unwrap(),
694                    receiver_id: "receiver.near".parse().unwrap(),
695                    actions: vec![
696                        NonDelegateAction::try_from(Action::Transfer(TransferAction {
697                            deposit: NearToken::from_yoctonear(1000),
698                        }))
699                        .unwrap(),
700                    ],
701                    nonce: 1,
702                    max_block_height: 1000,
703                    public_key: PublicKey::ED25519(ED25519PublicKey(
704                        [0; ED25519_PUBLIC_KEY_LENGTH],
705                    )),
706                },
707                signature: Signature::from_parts(crate::crypto::KeyType::ED25519, &[0u8; 64])
708                    .unwrap(),
709            })),
710            Action::DeterministicStateInit(Box::new(DeterministicStateInitAction {
711                state_init: DeterministicAccountStateInit::V1(DeterministicAccountStateInitV1 {
712                    code: GlobalContractIdentifier::AccountId("init.near".parse().unwrap()),
713                    data: btreemap.clone(),
714                }),
715                deposit: NearToken::from_yoctonear(5000000000),
716            })),
717        ];
718
719        let near_primitives_actions = vec![
720            npa::Action::CreateAccount(npa::CreateAccountAction {}),
721            npa::Action::DeployContract(npa::DeployContractAction {
722                code: vec![1, 2, 3],
723            }),
724            npa::Action::FunctionCall(Box::new(npa::FunctionCallAction {
725                method_name: "test".to_string(),
726                args: vec![4, 5, 6],
727                gas: Gas::from_gas(1000000),
728                deposit: NearToken::ZERO,
729            })),
730            npa::Action::Transfer(npa::TransferAction {
731                deposit: NearToken::from_yoctonear(1000000000),
732            }),
733            npa::Action::Stake(Box::new(npa::StakeAction {
734                stake: NearToken::from_yoctonear(100000000),
735                public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
736            })),
737            npa::Action::AddKey(Box::new(npa::AddKeyAction {
738                public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
739                access_key: near_primitives::account::AccessKey {
740                    nonce: 0,
741                    permission: near_primitives::account::AccessKeyPermission::FullAccess,
742                },
743            })),
744            npa::Action::DeleteKey(Box::new(npa::DeleteKeyAction {
745                public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
746            })),
747            npa::Action::DeleteAccount(npa::DeleteAccountAction {
748                beneficiary_id: "alice.near".parse().unwrap(),
749            }),
750            npa::Action::DeployGlobalContract(npa::DeployGlobalContractAction {
751                code: Arc::new([7, 8, 9]),
752                deploy_mode: npa::GlobalContractDeployMode::CodeHash,
753            }),
754            npa::Action::UseGlobalContract(Box::new(npa::UseGlobalContractAction {
755                contract_identifier: npa::GlobalContractIdentifier::AccountId(
756                    "global.near".parse().unwrap(),
757                ),
758            })),
759            npa::Action::Delegate(Box::new(npa::delegate::SignedDelegateAction {
760                delegate_action: npa::delegate::DelegateAction {
761                    sender_id: "sender.near".parse().unwrap(),
762                    receiver_id: "receiver.near".parse().unwrap(),
763                    actions: vec![npa::delegate::NonDelegateAction::try_from(
764                        npa::Action::Transfer(npa::TransferAction {
765                            deposit: NearToken::from_yoctonear(1000),
766                        }),
767                    )
768                    .unwrap()],
769                    nonce: 1,
770                    max_block_height: 1000,
771                    public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
772                },
773                signature: near_crypto::Signature::from_parts(
774                    near_crypto::KeyType::ED25519,
775                    &[0u8; 64],
776                )
777                .unwrap(),
778            })),
779            npa::Action::DeterministicStateInit(Box::new(npa::DeterministicStateInitAction {
780                state_init: npaDeterministicAccountStateInit::V1(
781                    npaDeterministicAccountStateInitV1 {
782                        code: npaGlobalContractIdentifier::AccountId("init.near".parse().unwrap()),
783                        data: btreemap,
784                    },
785                ),
786                deposit: NearToken::from_yoctonear(5000000000),
787            })),
788        ];
789
790        (local_actions, near_primitives_actions)
791    }
792
793    #[test]
794    fn test_action_serialization() {
795        let (local_actions, _) = get_actions();
796
797        for action in local_actions {
798            let serialized =
799                serde_json::to_string(&action).expect("Failed to serialize action to JSON");
800
801            let deserialized: Action =
802                serde_json::from_str(&serialized).expect("Failed to deserialize action from JSON");
803
804            assert_eq!(
805                action, deserialized,
806                "Serialization/Deserialization mismatch: original action: {action:?}, deserialized action: {deserialized:?}"
807            );
808        }
809    }
810
811    #[test]
812    fn test_action_borsh_serialization() {
813        let (local_actions, _) = get_actions();
814
815        for action in local_actions {
816            let serialized = borsh::to_vec(&action).expect("Failed to serialize action to borsh");
817
818            let deserialized: Action = Action::try_from_slice(&serialized)
819                .expect("Failed to deserialize action from borsh");
820
821            assert_eq!(
822                action, deserialized,
823                "Serialization/Deserialization mismatch: original action: {action:?}, deserialized action: {deserialized:?}"
824            );
825        }
826    }
827
828    #[test]
829    fn serialization_comparison_with_near_primitives() {
830        let (local_actions, near_primitives_actions) = get_actions();
831
832        assert_eq!(
833            local_actions.len(),
834            near_primitives_actions.len(),
835            "Action lists should have the same length"
836        );
837
838        for (local_action, np_action) in local_actions.iter().zip(near_primitives_actions.iter()) {
839            // Compare borsh serialization
840            let local_borsh =
841                borsh::to_vec(local_action).expect("Failed to serialize local action to borsh");
842            let np_borsh = borsh::to_vec(np_action)
843                .expect("Failed to serialize near_primitives action to borsh");
844
845            assert_eq!(local_borsh, np_borsh, "Borsh serialization mismatch");
846
847            // Compare serde JSON serialization
848            let local_json = serde_json::to_string(local_action)
849                .expect("Failed to serialize local action to JSON");
850            let np_json = serde_json::to_string(np_action)
851                .expect("Failed to serialize near_primitives action to JSON");
852
853            assert_eq!(local_json, np_json, "JSON serialization mismatch");
854        }
855    }
856}