1use std::collections::BTreeMap;
2use std::str::FromStr;
3
4use crate::AccountId;
5use base64::Engine;
6use base64::prelude::BASE64_STANDARD;
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 CreateAccount(CreateAccountAction),
23 DeployContract(DeployContractAction),
25 FunctionCall(Box<FunctionCallAction>),
27 Transfer(TransferAction),
29 Stake(Box<StakeAction>),
32 AddKey(Box<AddKeyAction>),
34 DeleteKey(Box<DeleteKeyAction>),
36 DeleteAccount(DeleteAccountAction),
38 Delegate(Box<SignedDelegateAction>),
40 DeployGlobalContract(DeployGlobalContractAction),
42 UseGlobalContract(Box<UseGlobalContractAction>),
44 DeterministicStateInit(Box<DeterministicStateInitAction>),
48
49 AddGasKey(Box<AddGasKeyAction>),
53 DeleteGasKey(Box<DeleteGasKeyAction>),
57 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.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 From<near_openapi_types::UseGlobalContractAction> for UseGlobalContractAction {
139 fn from(val: near_openapi_types::UseGlobalContractAction) -> Self {
140 let near_openapi_types::UseGlobalContractAction {
141 contract_identifier,
142 } = val;
143 Self {
144 contract_identifier: contract_identifier.into(),
145 }
146 }
147}
148
149#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
150pub struct CreateAccountAction {}
151
152impl From<near_openapi_types::CreateAccountAction> for CreateAccountAction {
153 fn from(_: near_openapi_types::CreateAccountAction) -> Self {
154 Self {}
155 }
156}
157
158#[serde_as]
159#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
160pub struct DeployContractAction {
161 #[serde_as(as = "Base64")]
162 pub code: Vec<u8>,
163}
164
165impl TryFrom<near_openapi_types::DeployContractAction> for DeployContractAction {
166 type Error = DataConversionError;
167 fn try_from(val: near_openapi_types::DeployContractAction) -> Result<Self, Self::Error> {
168 let near_openapi_types::DeployContractAction { code } = val;
169 Ok(Self {
170 code: BASE64_STANDARD.decode(code)?,
171 })
172 }
173}
174
175#[serde_as]
176#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
177pub struct FunctionCallAction {
178 pub method_name: String,
179 #[serde_as(as = "Base64")]
180 pub args: Vec<u8>,
181 #[serde(serialize_with = "near_gas_as_u64::serialize")]
182 pub gas: NearGas,
183 pub deposit: NearToken,
184}
185
186impl TryFrom<near_openapi_types::FunctionCallAction> for FunctionCallAction {
187 type Error = DataConversionError;
188 fn try_from(val: near_openapi_types::FunctionCallAction) -> Result<Self, Self::Error> {
189 let near_openapi_types::FunctionCallAction {
190 method_name,
191 args,
192 gas,
193 deposit,
194 } = val;
195 Ok(Self {
196 method_name,
197 args: BASE64_STANDARD.decode(args)?,
198 gas,
199 deposit,
200 })
201 }
202}
203
204#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
205pub struct TransferAction {
206 pub deposit: NearToken,
207}
208
209impl TryFrom<near_openapi_types::TransferAction> for TransferAction {
210 type Error = DataConversionError;
211 fn try_from(val: near_openapi_types::TransferAction) -> Result<Self, Self::Error> {
212 let near_openapi_types::TransferAction { deposit } = val;
213 Ok(Self { deposit })
214 }
215}
216
217#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
218pub struct StakeAction {
219 pub stake: NearToken,
221 pub public_key: PublicKey,
223}
224
225impl TryFrom<near_openapi_types::StakeAction> for StakeAction {
226 type Error = DataConversionError;
227 fn try_from(val: near_openapi_types::StakeAction) -> Result<Self, Self::Error> {
228 let near_openapi_types::StakeAction { public_key, stake } = val;
229 Ok(Self {
230 public_key: public_key.try_into()?,
231 stake,
232 })
233 }
234}
235
236#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
237pub struct AddGasKeyAction {
238 pub public_key: PublicKey,
239 pub num_nonces: u32,
240 pub permission: AccessKeyPermission,
241}
242
243impl TryFrom<near_openapi_types::AddGasKeyAction> for AddGasKeyAction {
244 type Error = DataConversionError;
245 fn try_from(val: near_openapi_types::AddGasKeyAction) -> Result<Self, Self::Error> {
246 let near_openapi_types::AddGasKeyAction {
247 public_key,
248 num_nonces,
249 permission,
250 } = val;
251 Ok(Self {
252 public_key: public_key.try_into()?,
253 num_nonces,
254 permission: permission.try_into()?,
255 })
256 }
257}
258
259#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
260pub struct DeleteGasKeyAction {
261 pub public_key: PublicKey,
262}
263
264impl TryFrom<near_openapi_types::DeleteGasKeyAction> for DeleteGasKeyAction {
265 type Error = DataConversionError;
266 fn try_from(val: near_openapi_types::DeleteGasKeyAction) -> Result<Self, Self::Error> {
267 let near_openapi_types::DeleteGasKeyAction { public_key } = val;
268 Ok(Self {
269 public_key: public_key.try_into()?,
270 })
271 }
272}
273
274#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
275pub struct TransferToGasKeyAction {
276 pub public_key: PublicKey,
277 pub deposit: NearToken,
278}
279
280impl TryFrom<near_openapi_types::TransferToGasKeyAction> for TransferToGasKeyAction {
281 type Error = DataConversionError;
282 fn try_from(val: near_openapi_types::TransferToGasKeyAction) -> Result<Self, Self::Error> {
283 let near_openapi_types::TransferToGasKeyAction {
284 public_key,
285 deposit,
286 } = val;
287 Ok(Self {
288 public_key: public_key.try_into()?,
289 deposit,
290 })
291 }
292}
293
294#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
295pub struct AddKeyAction {
296 pub public_key: PublicKey,
298 pub access_key: AccessKey,
300}
301
302impl TryFrom<near_openapi_types::AddKeyAction> for AddKeyAction {
303 type Error = DataConversionError;
304 fn try_from(val: near_openapi_types::AddKeyAction) -> Result<Self, Self::Error> {
305 let near_openapi_types::AddKeyAction {
306 public_key,
307 access_key,
308 } = val;
309 Ok(Self {
310 public_key: public_key.try_into()?,
311 access_key: access_key.try_into()?,
312 })
313 }
314}
315
316#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
317pub struct AccessKey {
318 pub nonce: U64,
322 pub permission: AccessKeyPermission,
324}
325
326impl TryFrom<near_openapi_types::AccessKeyView> for AccessKey {
327 type Error = DataConversionError;
328 fn try_from(val: near_openapi_types::AccessKeyView) -> Result<Self, Self::Error> {
329 let near_openapi_types::AccessKeyView { nonce, permission } = val;
330 Ok(Self {
331 nonce: U64(nonce),
332 permission: permission.try_into()?,
333 })
334 }
335}
336
337impl TryFrom<near_openapi_types::AccessKey> for AccessKey {
338 type Error = DataConversionError;
339 fn try_from(val: near_openapi_types::AccessKey) -> Result<Self, Self::Error> {
340 let near_openapi_types::AccessKey { nonce, permission } = val;
341 Ok(Self {
342 nonce: U64(nonce),
343 permission: permission.try_into()?,
344 })
345 }
346}
347
348#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
349pub enum AccessKeyPermission {
350 FunctionCall(FunctionCallPermission),
351 FullAccess,
354}
355
356impl TryFrom<near_openapi_types::AccessKeyPermissionView> for AccessKeyPermission {
357 type Error = DataConversionError;
358 fn try_from(val: near_openapi_types::AccessKeyPermissionView) -> Result<Self, Self::Error> {
359 match val {
360 near_openapi_types::AccessKeyPermissionView::FunctionCall {
361 allowance,
362 method_names,
363 receiver_id,
364 } => Ok(Self::FunctionCall(FunctionCallPermission {
365 allowance,
366 receiver_id,
367 method_names,
368 })),
369 near_openapi_types::AccessKeyPermissionView::FullAccess => Ok(Self::FullAccess),
370 }
371 }
372}
373
374impl TryFrom<near_openapi_types::AccessKeyPermission> for AccessKeyPermission {
375 type Error = DataConversionError;
376 fn try_from(val: near_openapi_types::AccessKeyPermission) -> Result<Self, Self::Error> {
377 match val {
378 near_openapi_types::AccessKeyPermission::FunctionCall(function_call_permission) => {
379 Ok(Self::FunctionCall(function_call_permission.try_into()?))
380 }
381 near_openapi_types::AccessKeyPermission::FullAccess => Ok(Self::FullAccess),
382 }
383 }
384}
385
386#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
387pub struct FunctionCallPermission {
388 pub allowance: Option<NearToken>,
389 pub receiver_id: String,
390 pub method_names: Vec<String>,
391}
392
393impl TryFrom<near_openapi_types::FunctionCallPermission> for FunctionCallPermission {
394 type Error = DataConversionError;
395 fn try_from(val: near_openapi_types::FunctionCallPermission) -> Result<Self, Self::Error> {
396 let near_openapi_types::FunctionCallPermission {
397 allowance,
398 receiver_id,
399 method_names,
400 } = val;
401 Ok(Self {
402 allowance,
403 receiver_id,
404 method_names,
405 })
406 }
407}
408
409#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
410pub struct DeleteKeyAction {
411 pub public_key: PublicKey,
413}
414
415impl TryFrom<near_openapi_types::DeleteKeyAction> for DeleteKeyAction {
416 type Error = DataConversionError;
417 fn try_from(val: near_openapi_types::DeleteKeyAction) -> Result<Self, Self::Error> {
418 let near_openapi_types::DeleteKeyAction { public_key } = val;
419 Ok(Self {
420 public_key: public_key.try_into()?,
421 })
422 }
423}
424
425#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
426pub struct DeleteAccountAction {
427 pub beneficiary_id: AccountId,
428}
429
430impl From<near_openapi_types::DeleteAccountAction> for DeleteAccountAction {
431 fn from(val: near_openapi_types::DeleteAccountAction) -> Self {
432 let near_openapi_types::DeleteAccountAction { beneficiary_id } = val;
433 Self { beneficiary_id }
434 }
435}
436
437#[derive(
438 BorshSerialize,
439 BorshDeserialize,
440 serde::Serialize,
441 serde::Deserialize,
442 PartialEq,
443 Eq,
444 Clone,
445 Debug,
446)]
447#[repr(u8)]
448pub enum GlobalContractDeployMode {
449 CodeHash,
453 AccountId,
457}
458
459impl From<near_openapi_types::GlobalContractDeployMode> for GlobalContractDeployMode {
460 fn from(val: near_openapi_types::GlobalContractDeployMode) -> Self {
461 match val {
462 near_openapi_types::GlobalContractDeployMode::CodeHash => Self::CodeHash,
463 near_openapi_types::GlobalContractDeployMode::AccountId => Self::AccountId,
464 }
465 }
466}
467#[derive(Serialize, Deserialize, Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
468pub enum GlobalContractIdentifier {
469 CodeHash(CryptoHash),
470 AccountId(AccountId),
471}
472
473impl From<near_openapi_types::GlobalContractIdentifier> for GlobalContractIdentifier {
474 fn from(val: near_openapi_types::GlobalContractIdentifier) -> Self {
475 match val {
476 near_openapi_types::GlobalContractIdentifier::CodeHash(code_hash) => {
477 Self::CodeHash(code_hash.into())
478 }
479 near_openapi_types::GlobalContractIdentifier::AccountId(account_id) => {
480 Self::AccountId(account_id)
481 }
482 }
483 }
484}
485
486impl From<near_openapi_types::GlobalContractIdentifierView> for GlobalContractIdentifier {
487 fn from(val: near_openapi_types::GlobalContractIdentifierView) -> Self {
488 match val {
489 near_openapi_types::GlobalContractIdentifierView::Hash(code_hash) => {
490 Self::CodeHash(code_hash.into())
491 }
492 near_openapi_types::GlobalContractIdentifierView::AccountId(account_id) => {
493 Self::AccountId(account_id)
494 }
495 }
496 }
497}
498
499impl TryFrom<near_openapi_types::ActionView> for Action {
500 type Error = DataConversionError;
501 fn try_from(val: near_openapi_types::ActionView) -> Result<Self, Self::Error> {
502 match val {
503 near_openapi_types::ActionView::DeterministicStateInit {
504 code,
505 data,
506 deposit,
507 } => Ok(Self::DeterministicStateInit(Box::new(
508 DeterministicStateInitAction {
509 state_init: DeterministicAccountStateInit::V1(
510 DeterministicAccountStateInitV1 {
511 code: code.into(),
512 data: data
513 .into_iter()
514 .map(|(k, v)| {
515 Ok::<(Vec<u8>, Vec<u8>), DataConversionError>((
516 BASE64_STANDARD.decode(k)?,
517 BASE64_STANDARD.decode(v)?,
518 ))
519 })
520 .collect::<Result<BTreeMap<Vec<u8>, Vec<u8>>, _>>()?,
521 },
522 ),
523 deposit,
524 },
525 ))),
526 near_openapi_types::ActionView::CreateAccount => {
527 Ok(Self::CreateAccount(CreateAccountAction {}))
528 }
529 near_openapi_types::ActionView::DeployContract { code } => {
530 Ok(Self::DeployContract(DeployContractAction {
531 code: BASE64_STANDARD.decode(code)?,
532 }))
533 }
534 near_openapi_types::ActionView::FunctionCall {
535 method_name,
536 args,
537 gas,
538 deposit,
539 } => Ok(Self::FunctionCall(Box::new(FunctionCallAction {
540 method_name,
541 args: BASE64_STANDARD.decode(args.0)?,
542 gas,
543 deposit,
544 }))),
545 near_openapi_types::ActionView::Transfer { deposit } => {
546 Ok(Self::Transfer(TransferAction { deposit }))
547 }
548 near_openapi_types::ActionView::Stake { public_key, stake } => {
549 Ok(Self::Stake(Box::new(StakeAction {
550 public_key: public_key.try_into()?,
551 stake,
552 })))
553 }
554 near_openapi_types::ActionView::AddKey {
555 access_key,
556 public_key,
557 } => Ok(Self::AddKey(Box::new(AddKeyAction {
558 public_key: public_key.try_into()?,
559 access_key: access_key.try_into()?,
560 }))),
561 near_openapi_types::ActionView::DeleteKey { public_key } => {
562 Ok(Self::DeleteKey(Box::new(DeleteKeyAction {
563 public_key: public_key.try_into()?,
564 })))
565 }
566 near_openapi_types::ActionView::DeleteAccount { beneficiary_id } => {
567 Ok(Self::DeleteAccount(DeleteAccountAction { beneficiary_id }))
568 }
569 near_openapi_types::ActionView::Delegate {
570 delegate_action,
571 signature,
572 } => Ok(Self::Delegate(Box::new(SignedDelegateAction {
573 delegate_action: delegate_action.try_into()?,
574 signature: Signature::from_str(&signature)?,
575 }))),
576 near_openapi_types::ActionView::DeployGlobalContract { code } => {
577 Ok(Self::DeployGlobalContract(DeployGlobalContractAction {
578 code: BASE64_STANDARD.decode(code)?,
579 deploy_mode: GlobalContractDeployMode::CodeHash,
580 }))
581 }
582 near_openapi_types::ActionView::DeployGlobalContractByAccountId { code } => {
583 Ok(Self::DeployGlobalContract(DeployGlobalContractAction {
584 code: BASE64_STANDARD.decode(code)?,
585 deploy_mode: GlobalContractDeployMode::AccountId,
586 }))
587 }
588 near_openapi_types::ActionView::UseGlobalContract { code_hash } => {
589 Ok(Self::UseGlobalContract(Box::new(UseGlobalContractAction {
590 contract_identifier: GlobalContractIdentifier::CodeHash(code_hash.into()),
591 })))
592 }
593 near_openapi_types::ActionView::UseGlobalContractByAccountId { account_id } => {
594 Ok(Self::UseGlobalContract(Box::new(UseGlobalContractAction {
595 contract_identifier: GlobalContractIdentifier::AccountId(account_id),
596 })))
597 }
598 near_openapi_types::ActionView::AddGasKey {
599 num_nonces,
600 permission,
601 public_key,
602 } => Ok(Self::AddGasKey(Box::new(AddGasKeyAction {
603 public_key: public_key.try_into()?,
604 num_nonces,
605 permission: permission.try_into()?,
606 }))),
607 near_openapi_types::ActionView::DeleteGasKey { public_key } => {
608 Ok(Self::DeleteGasKey(Box::new(DeleteGasKeyAction {
609 public_key: public_key.try_into()?,
610 })))
611 }
612 near_openapi_types::ActionView::TransferToGasKey {
613 amount: deposit,
614 public_key,
615 } => Ok(Self::TransferToGasKey(Box::new(TransferToGasKeyAction {
616 public_key: public_key.try_into()?,
617 deposit,
618 }))),
619 }
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use std::collections::BTreeMap;
626 use std::sync::Arc;
627
628 use super::*;
629 use crate::crypto::{ED25519_PUBLIC_KEY_LENGTH, public_key::ED25519PublicKey};
630 use crate::transaction::delegate_action::{DelegateAction, NonDelegateAction};
631 use near_primitives::action as npa;
632 use near_primitives::deterministic_account_id::{
633 DeterministicAccountStateInit as npaDeterministicAccountStateInit,
634 DeterministicAccountStateInitV1 as npaDeterministicAccountStateInitV1,
635 };
636 use near_primitives::gas::Gas;
637 use near_primitives::global_contract::GlobalContractIdentifier as npaGlobalContractIdentifier;
638 use serde_json;
639
640 fn get_actions() -> (Vec<Action>, Vec<npa::Action>) {
641 let btreemap = BTreeMap::from([(b"key".to_vec(), b"value".to_vec())]);
642
643 let local_actions = vec![
644 Action::CreateAccount(CreateAccountAction {}),
645 Action::DeployContract(DeployContractAction {
646 code: vec![1, 2, 3],
647 }),
648 Action::FunctionCall(Box::new(FunctionCallAction {
649 method_name: "test".to_string(),
650 args: vec![4, 5, 6],
651 gas: NearGas::from_gas(1000000),
652 deposit: NearToken::from_yoctonear(0),
653 })),
654 Action::Transfer(TransferAction {
655 deposit: NearToken::from_yoctonear(1000000000),
656 }),
657 Action::Stake(Box::new(StakeAction {
658 stake: NearToken::from_yoctonear(100000000),
659 public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
660 })),
661 Action::AddKey(Box::new(AddKeyAction {
662 public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
663 access_key: AccessKey {
664 nonce: U64(0),
665 permission: AccessKeyPermission::FullAccess,
666 },
667 })),
668 Action::DeleteKey(Box::new(DeleteKeyAction {
669 public_key: PublicKey::ED25519(ED25519PublicKey([0; ED25519_PUBLIC_KEY_LENGTH])),
670 })),
671 Action::DeleteAccount(DeleteAccountAction {
672 beneficiary_id: "alice.near".parse().unwrap(),
673 }),
674 Action::DeployGlobalContract(DeployGlobalContractAction {
675 code: vec![7, 8, 9],
676 deploy_mode: GlobalContractDeployMode::CodeHash,
677 }),
678 Action::UseGlobalContract(Box::new(UseGlobalContractAction {
679 contract_identifier: GlobalContractIdentifier::AccountId(
680 "global.near".parse().unwrap(),
681 ),
682 })),
683 Action::Delegate(Box::new(SignedDelegateAction {
684 delegate_action: DelegateAction {
685 sender_id: "sender.near".parse().unwrap(),
686 receiver_id: "receiver.near".parse().unwrap(),
687 actions: vec![
688 NonDelegateAction::try_from(Action::Transfer(TransferAction {
689 deposit: NearToken::from_yoctonear(1000),
690 }))
691 .unwrap(),
692 ],
693 nonce: 1,
694 max_block_height: 1000,
695 public_key: PublicKey::ED25519(ED25519PublicKey(
696 [0; ED25519_PUBLIC_KEY_LENGTH],
697 )),
698 },
699 signature: Signature::from_parts(crate::crypto::KeyType::ED25519, &[0u8; 64])
700 .unwrap(),
701 })),
702 Action::DeterministicStateInit(Box::new(DeterministicStateInitAction {
703 state_init: DeterministicAccountStateInit::V1(DeterministicAccountStateInitV1 {
704 code: GlobalContractIdentifier::AccountId("init.near".parse().unwrap()),
705 data: btreemap.clone(),
706 }),
707 deposit: NearToken::from_yoctonear(5000000000),
708 })),
709 ];
710
711 let near_primitives_actions = vec![
712 npa::Action::CreateAccount(npa::CreateAccountAction {}),
713 npa::Action::DeployContract(npa::DeployContractAction {
714 code: vec![1, 2, 3],
715 }),
716 npa::Action::FunctionCall(Box::new(npa::FunctionCallAction {
717 method_name: "test".to_string(),
718 args: vec![4, 5, 6],
719 gas: Gas::from_gas(1000000),
720 deposit: NearToken::ZERO,
721 })),
722 npa::Action::Transfer(npa::TransferAction {
723 deposit: NearToken::from_yoctonear(1000000000),
724 }),
725 npa::Action::Stake(Box::new(npa::StakeAction {
726 stake: NearToken::from_yoctonear(100000000),
727 public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
728 })),
729 npa::Action::AddKey(Box::new(npa::AddKeyAction {
730 public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
731 access_key: near_primitives::account::AccessKey {
732 nonce: 0,
733 permission: near_primitives::account::AccessKeyPermission::FullAccess,
734 },
735 })),
736 npa::Action::DeleteKey(Box::new(npa::DeleteKeyAction {
737 public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
738 })),
739 npa::Action::DeleteAccount(npa::DeleteAccountAction {
740 beneficiary_id: "alice.near".parse().unwrap(),
741 }),
742 npa::Action::DeployGlobalContract(npa::DeployGlobalContractAction {
743 code: Arc::new([7, 8, 9]),
744 deploy_mode: npa::GlobalContractDeployMode::CodeHash,
745 }),
746 npa::Action::UseGlobalContract(Box::new(npa::UseGlobalContractAction {
747 contract_identifier: npa::GlobalContractIdentifier::AccountId(
748 "global.near".parse().unwrap(),
749 ),
750 })),
751 npa::Action::Delegate(Box::new(npa::delegate::SignedDelegateAction {
752 delegate_action: npa::delegate::DelegateAction {
753 sender_id: "sender.near".parse().unwrap(),
754 receiver_id: "receiver.near".parse().unwrap(),
755 actions: vec![
756 npa::delegate::NonDelegateAction::try_from(npa::Action::Transfer(
757 npa::TransferAction {
758 deposit: NearToken::from_yoctonear(1000),
759 },
760 ))
761 .unwrap(),
762 ],
763 nonce: 1,
764 max_block_height: 1000,
765 public_key: near_crypto::PublicKey::empty(near_crypto::KeyType::ED25519),
766 },
767 signature: near_crypto::Signature::from_parts(
768 near_crypto::KeyType::ED25519,
769 &[0u8; 64],
770 )
771 .unwrap(),
772 })),
773 npa::Action::DeterministicStateInit(Box::new(npa::DeterministicStateInitAction {
774 state_init: npaDeterministicAccountStateInit::V1(
775 npaDeterministicAccountStateInitV1 {
776 code: npaGlobalContractIdentifier::AccountId("init.near".parse().unwrap()),
777 data: btreemap,
778 },
779 ),
780 deposit: NearToken::from_yoctonear(5000000000),
781 })),
782 ];
783
784 (local_actions, near_primitives_actions)
785 }
786
787 #[test]
788 fn test_action_serialization() {
789 let (local_actions, _) = get_actions();
790
791 for action in local_actions {
792 let serialized =
793 serde_json::to_string(&action).expect("Failed to serialize action to JSON");
794
795 let deserialized: Action =
796 serde_json::from_str(&serialized).expect("Failed to deserialize action from JSON");
797
798 assert_eq!(
799 action, deserialized,
800 "Serialization/Deserialization mismatch: original action: {action:?}, deserialized action: {deserialized:?}"
801 );
802 }
803 }
804
805 #[test]
806 fn test_action_borsh_serialization() {
807 let (local_actions, _) = get_actions();
808
809 for action in local_actions {
810 let serialized = borsh::to_vec(&action).expect("Failed to serialize action to borsh");
811
812 let deserialized: Action = Action::try_from_slice(&serialized)
813 .expect("Failed to deserialize action from borsh");
814
815 assert_eq!(
816 action, deserialized,
817 "Serialization/Deserialization mismatch: original action: {action:?}, deserialized action: {deserialized:?}"
818 );
819 }
820 }
821
822 #[test]
823 fn serialization_comparison_with_near_primitives() {
824 let (local_actions, near_primitives_actions) = get_actions();
825
826 assert_eq!(
827 local_actions.len(),
828 near_primitives_actions.len(),
829 "Action lists should have the same length"
830 );
831
832 for (local_action, np_action) in local_actions.iter().zip(near_primitives_actions.iter()) {
833 let local_borsh =
835 borsh::to_vec(local_action).expect("Failed to serialize local action to borsh");
836 let np_borsh = borsh::to_vec(np_action)
837 .expect("Failed to serialize near_primitives action to borsh");
838
839 assert_eq!(local_borsh, np_borsh, "Borsh serialization mismatch");
840
841 }
847 }
848}