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 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.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 pub stake: NearToken,
222 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 pub public_key: PublicKey,
299 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 pub nonce: U64,
323 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 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 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 CodeHash,
454 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 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 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}