1use alloc::collections::BTreeMap;
2use alloc::string::ToString;
3
4use miden_core::LexicographicWord;
5
6use crate::asset::{Asset, AssetVault};
7use crate::utils::serde::{
8 ByteReader,
9 ByteWriter,
10 Deserializable,
11 DeserializationError,
12 Serializable,
13};
14use crate::{AccountError, Felt, Hasher, Word, ZERO};
15
16mod account_id;
17pub use account_id::{
18 AccountId,
19 AccountIdPrefix,
20 AccountIdPrefixV0,
21 AccountIdV0,
22 AccountIdVersion,
23 AccountStorageMode,
24 AccountType,
25};
26
27pub mod auth;
28
29mod builder;
30pub use builder::AccountBuilder;
31
32pub mod code;
33pub use code::AccountCode;
34pub use code::procedure::AccountProcedureInfo;
35
36pub mod component;
37pub use component::{
38 AccountComponent,
39 AccountComponentMetadata,
40 AccountComponentTemplate,
41 FeltRepresentation,
42 InitStorageData,
43 MapEntry,
44 MapRepresentation,
45 PlaceholderTypeRequirement,
46 StorageEntry,
47 StorageValueName,
48 StorageValueNameError,
49 TemplateType,
50 TemplateTypeError,
51 WordRepresentation,
52};
53
54pub mod delta;
55pub use delta::{
56 AccountDelta,
57 AccountStorageDelta,
58 AccountVaultDelta,
59 FungibleAssetDelta,
60 NonFungibleAssetDelta,
61 NonFungibleDeltaAction,
62 StorageMapDelta,
63};
64
65mod storage;
66pub use storage::{
67 AccountStorage,
68 AccountStorageHeader,
69 PartialStorage,
70 PartialStorageMap,
71 SlotName,
72 StorageMap,
73 StorageMapWitness,
74 StorageSlot,
75 StorageSlotType,
76};
77
78mod header;
79pub use header::AccountHeader;
80
81mod file;
82pub use file::AccountFile;
83
84mod partial;
85pub use partial::PartialAccount;
86
87#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct Account {
110 id: AccountId,
111 vault: AssetVault,
112 storage: AccountStorage,
113 code: AccountCode,
114 nonce: Felt,
115 seed: Option<Word>,
116}
117
118impl Account {
119 pub fn new(
132 id: AccountId,
133 vault: AssetVault,
134 storage: AccountStorage,
135 code: AccountCode,
136 nonce: Felt,
137 seed: Option<Word>,
138 ) -> Result<Self, AccountError> {
139 validate_account_seed(id, code.commitment(), storage.commitment(), seed, nonce)?;
140
141 Ok(Self::new_unchecked(id, vault, storage, code, nonce, seed))
142 }
143
144 pub fn new_unchecked(
151 id: AccountId,
152 vault: AssetVault,
153 storage: AccountStorage,
154 code: AccountCode,
155 nonce: Felt,
156 seed: Option<Word>,
157 ) -> Self {
158 Self { id, vault, storage, code, nonce, seed }
159 }
160
161 pub(super) fn initialize_from_components(
203 account_type: AccountType,
204 components: &[AccountComponent],
205 ) -> Result<(AccountCode, AccountStorage), AccountError> {
206 validate_components_support_account_type(components, account_type)?;
207
208 let code = AccountCode::from_components_unchecked(components, account_type)?;
209 let storage = AccountStorage::from_components(components, account_type)?;
210
211 Ok((code, storage))
212 }
213
214 pub fn builder(init_seed: [u8; 32]) -> AccountBuilder {
219 AccountBuilder::new(init_seed)
220 }
221
222 pub fn commitment(&self) -> Word {
231 hash_account(
232 self.id,
233 self.nonce,
234 self.vault.root(),
235 self.storage.commitment(),
236 self.code.commitment(),
237 )
238 }
239
240 pub fn initial_commitment(&self) -> Word {
250 if self.is_new() {
251 Word::empty()
252 } else {
253 self.commitment()
254 }
255 }
256
257 pub fn id(&self) -> AccountId {
259 self.id
260 }
261
262 pub fn account_type(&self) -> AccountType {
264 self.id.account_type()
265 }
266
267 pub fn vault(&self) -> &AssetVault {
269 &self.vault
270 }
271
272 pub fn storage(&self) -> &AccountStorage {
274 &self.storage
275 }
276
277 pub fn code(&self) -> &AccountCode {
279 &self.code
280 }
281
282 pub fn nonce(&self) -> Felt {
284 self.nonce
285 }
286
287 pub fn seed(&self) -> Option<Word> {
291 self.seed
292 }
293
294 pub fn is_faucet(&self) -> bool {
296 self.id.is_faucet()
297 }
298
299 pub fn is_regular_account(&self) -> bool {
301 self.id.is_regular_account()
302 }
303
304 pub fn has_public_state(&self) -> bool {
307 self.id().has_public_state()
308 }
309
310 pub fn is_public(&self) -> bool {
312 self.id().is_public()
313 }
314
315 pub fn is_private(&self) -> bool {
317 self.id().is_private()
318 }
319
320 pub fn is_network(&self) -> bool {
322 self.id().is_network()
323 }
324
325 pub fn is_new(&self) -> bool {
330 self.nonce == ZERO
331 }
332
333 pub fn into_parts(
335 self,
336 ) -> (AccountId, AssetVault, AccountStorage, AccountCode, Felt, Option<Word>) {
337 (self.id, self.vault, self.storage, self.code, self.nonce, self.seed)
338 }
339
340 pub fn apply_delta(&mut self, delta: &AccountDelta) -> Result<(), AccountError> {
356 if delta.is_full_state() {
357 return Err(AccountError::ApplyFullStateDeltaToAccount);
358 }
359
360 self.vault
363 .apply_delta(delta.vault())
364 .map_err(AccountError::AssetVaultUpdateError)?;
365
366 self.storage.apply_delta(delta.storage())?;
368
369 self.increment_nonce(delta.nonce_delta())?;
371
372 Ok(())
373 }
374
375 pub fn increment_nonce(&mut self, nonce_delta: Felt) -> Result<(), AccountError> {
382 let new_nonce = self.nonce + nonce_delta;
383
384 if new_nonce.as_int() < self.nonce.as_int() {
385 return Err(AccountError::NonceOverflow {
386 current: self.nonce,
387 increment: nonce_delta,
388 new: new_nonce,
389 });
390 }
391
392 self.nonce = new_nonce;
393
394 if !self.is_new() {
399 self.seed = None;
400 }
401
402 Ok(())
403 }
404
405 #[cfg(any(feature = "testing", test))]
409 pub fn vault_mut(&mut self) -> &mut AssetVault {
411 &mut self.vault
412 }
413
414 #[cfg(any(feature = "testing", test))]
415 pub fn storage_mut(&mut self) -> &mut AccountStorage {
417 &mut self.storage
418 }
419}
420
421impl TryFrom<Account> for AccountDelta {
422 type Error = AccountError;
423
424 fn try_from(account: Account) -> Result<Self, Self::Error> {
433 let Account { id, vault, storage, code, nonce, seed } = account;
434
435 if seed.is_some() {
436 return Err(AccountError::DeltaFromAccountWithSeed);
437 }
438
439 let mut value_slots = BTreeMap::new();
440 let mut map_slots = BTreeMap::new();
441
442 for (slot_idx, slot) in (0..u8::MAX).zip(storage.into_slots().into_iter()) {
443 match slot {
444 StorageSlot::Value(word) => {
445 value_slots.insert(slot_idx, word);
446 },
447 StorageSlot::Map(storage_map) => {
448 let map_delta = StorageMapDelta::new(
449 storage_map
450 .into_entries()
451 .into_iter()
452 .map(|(key, value)| (LexicographicWord::from(key), value))
453 .collect(),
454 );
455 map_slots.insert(slot_idx, map_delta);
456 },
457 }
458 }
459 let storage_delta = AccountStorageDelta::from_parts(value_slots, map_slots)
460 .expect("value and map slots from account storage should not overlap");
461
462 let mut fungible_delta = FungibleAssetDelta::default();
463 let mut non_fungible_delta = NonFungibleAssetDelta::default();
464 for asset in vault.assets() {
465 match asset {
467 Asset::Fungible(fungible_asset) => {
468 fungible_delta
469 .add(fungible_asset)
470 .expect("delta should allow representing valid fungible assets");
471 },
472 Asset::NonFungible(non_fungible_asset) => {
473 non_fungible_delta
474 .add(non_fungible_asset)
475 .expect("delta should allow representing valid non-fungible assets");
476 },
477 }
478 }
479 let vault_delta = AccountVaultDelta::new(fungible_delta, non_fungible_delta);
480
481 let nonce_delta = nonce;
484
485 let delta = AccountDelta::new(id, storage_delta, vault_delta, nonce_delta)
488 .expect("nonce_delta should be greater than 0")
489 .with_code(Some(code));
490
491 Ok(delta)
492 }
493}
494
495impl Serializable for Account {
499 fn write_into<W: ByteWriter>(&self, target: &mut W) {
500 let Account { id, vault, storage, code, nonce, seed } = self;
501
502 id.write_into(target);
503 vault.write_into(target);
504 storage.write_into(target);
505 code.write_into(target);
506 nonce.write_into(target);
507 seed.write_into(target);
508 }
509
510 fn get_size_hint(&self) -> usize {
511 self.id.get_size_hint()
512 + self.vault.get_size_hint()
513 + self.storage.get_size_hint()
514 + self.code.get_size_hint()
515 + self.nonce.get_size_hint()
516 + self.seed.get_size_hint()
517 }
518}
519
520impl Deserializable for Account {
521 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
522 let id = AccountId::read_from(source)?;
523 let vault = AssetVault::read_from(source)?;
524 let storage = AccountStorage::read_from(source)?;
525 let code = AccountCode::read_from(source)?;
526 let nonce = Felt::read_from(source)?;
527 let seed = <Option<Word>>::read_from(source)?;
528
529 Self::new(id, vault, storage, code, nonce, seed)
530 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
531 }
532}
533
534pub fn hash_account(
543 id: AccountId,
544 nonce: Felt,
545 vault_root: Word,
546 storage_commitment: Word,
547 code_commitment: Word,
548) -> Word {
549 let mut elements = [ZERO; 16];
550 elements[0] = id.suffix();
551 elements[1] = id.prefix().as_felt();
552 elements[3] = nonce;
553 elements[4..8].copy_from_slice(&*vault_root);
554 elements[8..12].copy_from_slice(&*storage_commitment);
555 elements[12..].copy_from_slice(&*code_commitment);
556 Hasher::hash_elements(&elements)
557}
558
559pub(super) fn validate_account_seed(
564 id: AccountId,
565 code_commitment: Word,
566 storage_commitment: Word,
567 seed: Option<Word>,
568 nonce: Felt,
569) -> Result<(), AccountError> {
570 let account_is_new = nonce == ZERO;
571
572 match (account_is_new, seed) {
573 (true, Some(seed)) => {
574 let account_id =
575 AccountId::new(seed, id.version(), code_commitment, storage_commitment)
576 .map_err(AccountError::SeedConvertsToInvalidAccountId)?;
577
578 if account_id != id {
579 return Err(AccountError::AccountIdSeedMismatch {
580 expected: id,
581 actual: account_id,
582 });
583 }
584
585 Ok(())
586 },
587 (true, None) => Err(AccountError::NewAccountMissingSeed),
588 (false, Some(_)) => Err(AccountError::ExistingAccountWithSeed),
589 (false, None) => Ok(()),
590 }
591}
592
593fn validate_components_support_account_type(
595 components: &[AccountComponent],
596 account_type: AccountType,
597) -> Result<(), AccountError> {
598 for (component_index, component) in components.iter().enumerate() {
599 if !component.supports_type(account_type) {
600 return Err(AccountError::UnsupportedComponentForAccountType {
601 account_type,
602 component_index,
603 });
604 }
605 }
606
607 Ok(())
608}
609
610#[cfg(test)]
614mod tests {
615 use alloc::vec::Vec;
616
617 use assert_matches::assert_matches;
618 use miden_assembly::Assembler;
619 use miden_core::FieldElement;
620 use miden_crypto::utils::{Deserializable, Serializable};
621 use miden_crypto::{Felt, Word};
622
623 use super::{
624 AccountCode,
625 AccountDelta,
626 AccountId,
627 AccountStorage,
628 AccountStorageDelta,
629 AccountVaultDelta,
630 };
631 use crate::AccountError;
632 use crate::account::AccountStorageMode::Network;
633 use crate::account::{
634 Account,
635 AccountBuilder,
636 AccountComponent,
637 AccountIdVersion,
638 AccountType,
639 PartialAccount,
640 StorageMap,
641 StorageMapDelta,
642 StorageSlot,
643 };
644 use crate::asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset};
645 use crate::testing::account_id::{
646 ACCOUNT_ID_PRIVATE_SENDER,
647 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
648 };
649 use crate::testing::add_component::AddComponent;
650 use crate::testing::noop_auth_component::NoopAuthComponent;
651 use crate::testing::storage::AccountStorageDeltaBuilder;
652
653 #[test]
654 fn test_serde_account() {
655 let init_nonce = Felt::new(1);
656 let asset_0 = FungibleAsset::mock(99);
657 let word = Word::from([1, 2, 3, 4u32]);
658 let storage_slot = StorageSlot::Value(word);
659 let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
660
661 let serialized = account.to_bytes();
662 let deserialized = Account::read_from_bytes(&serialized).unwrap();
663 assert_eq!(deserialized, account);
664 }
665
666 #[test]
667 fn test_serde_account_delta() {
668 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
669 let nonce_delta = Felt::new(2);
670 let asset_0 = FungibleAsset::mock(15);
671 let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]);
672 let storage_delta = AccountStorageDeltaBuilder::new()
673 .add_cleared_items([0])
674 .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))])
675 .build()
676 .unwrap();
677 let account_delta = build_account_delta(
678 account_id,
679 vec![asset_1],
680 vec![asset_0],
681 nonce_delta,
682 storage_delta,
683 );
684
685 let serialized = account_delta.to_bytes();
686 let deserialized = AccountDelta::read_from_bytes(&serialized).unwrap();
687 assert_eq!(deserialized, account_delta);
688 }
689
690 #[test]
691 fn valid_account_delta_is_correctly_applied() {
692 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
694 let init_nonce = Felt::new(1);
695 let asset_0 = FungibleAsset::mock(100);
696 let asset_1 = NonFungibleAsset::mock(&[1, 2, 3]);
697
698 let storage_slot_value_0 = StorageSlot::Value(Word::from([1, 2, 3, 4u32]));
700 let storage_slot_value_1 = StorageSlot::Value(Word::from([5, 6, 7, 8u32]));
701 let mut storage_map = StorageMap::with_entries([
702 (
703 Word::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
704 Word::from([
705 Felt::new(1_u64),
706 Felt::new(2_u64),
707 Felt::new(3_u64),
708 Felt::new(4_u64),
709 ]),
710 ),
711 (
712 Word::new([Felt::new(105), Felt::new(106), Felt::new(107), Felt::new(108)]),
713 Word::new([Felt::new(5_u64), Felt::new(6_u64), Felt::new(7_u64), Felt::new(8_u64)]),
714 ),
715 ])
716 .unwrap();
717 let storage_slot_map = StorageSlot::Map(storage_map.clone());
718
719 let mut account = build_account(
720 vec![asset_0],
721 init_nonce,
722 vec![storage_slot_value_0, storage_slot_value_1, storage_slot_map],
723 );
724
725 let new_map_entry = (
727 Word::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
728 [Felt::new(9_u64), Felt::new(10_u64), Felt::new(11_u64), Felt::new(12_u64)],
729 );
730
731 let updated_map =
732 StorageMapDelta::from_iters([], [(new_map_entry.0, new_map_entry.1.into())]);
733 storage_map.insert(new_map_entry.0, new_map_entry.1.into()).unwrap();
734
735 let final_nonce = Felt::new(2);
737 let storage_delta = AccountStorageDeltaBuilder::new()
738 .add_cleared_items([0])
739 .add_updated_values([(1, Word::from([1, 2, 3, 4u32]))])
740 .add_updated_maps([(2, updated_map)])
741 .build()
742 .unwrap();
743 let account_delta = build_account_delta(
744 account_id,
745 vec![asset_1],
746 vec![asset_0],
747 final_nonce - init_nonce,
748 storage_delta,
749 );
750
751 account.apply_delta(&account_delta).unwrap();
753
754 let final_account = build_account(
755 vec![asset_1],
756 final_nonce,
757 vec![
758 StorageSlot::Value(Word::empty()),
759 StorageSlot::Value(Word::from([1, 2, 3, 4u32])),
760 StorageSlot::Map(storage_map),
761 ],
762 );
763
764 assert_eq!(account, final_account);
766 }
767
768 #[test]
769 #[should_panic]
770 fn valid_account_delta_with_unchanged_nonce() {
771 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
773 let init_nonce = Felt::new(1);
774 let asset = FungibleAsset::mock(110);
775 let mut account =
776 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::empty())]);
777
778 let storage_delta = AccountStorageDeltaBuilder::new()
780 .add_cleared_items([0])
781 .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))])
782 .build()
783 .unwrap();
784 let account_delta =
785 build_account_delta(account_id, vec![], vec![asset], init_nonce, storage_delta);
786
787 account.apply_delta(&account_delta).unwrap()
789 }
790
791 #[test]
792 #[should_panic]
793 fn valid_account_delta_with_decremented_nonce() {
794 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
796 let init_nonce = Felt::new(2);
797 let asset = FungibleAsset::mock(100);
798 let mut account =
799 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::empty())]);
800
801 let final_nonce = Felt::new(1);
803 let storage_delta = AccountStorageDeltaBuilder::new()
804 .add_cleared_items([0])
805 .add_updated_values([(1_u8, Word::from([1, 2, 3, 4u32]))])
806 .build()
807 .unwrap();
808 let account_delta =
809 build_account_delta(account_id, vec![], vec![asset], final_nonce, storage_delta);
810
811 account.apply_delta(&account_delta).unwrap()
813 }
814
815 #[test]
816 fn empty_account_delta_with_incremented_nonce() {
817 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
819 let init_nonce = Felt::new(1);
820 let word = Word::from([1, 2, 3, 4u32]);
821 let storage_slot = StorageSlot::Value(word);
822 let mut account = build_account(vec![], init_nonce, vec![storage_slot]);
823
824 let nonce_delta = Felt::new(1);
826 let account_delta = AccountDelta::new(
827 account_id,
828 AccountStorageDelta::new(),
829 AccountVaultDelta::default(),
830 nonce_delta,
831 )
832 .unwrap();
833
834 account.apply_delta(&account_delta).unwrap()
836 }
837
838 pub fn build_account_delta(
839 account_id: AccountId,
840 added_assets: Vec<Asset>,
841 removed_assets: Vec<Asset>,
842 nonce_delta: Felt,
843 storage_delta: AccountStorageDelta,
844 ) -> AccountDelta {
845 let vault_delta = AccountVaultDelta::from_iters(added_assets, removed_assets);
846 AccountDelta::new(account_id, storage_delta, vault_delta, nonce_delta).unwrap()
847 }
848
849 pub fn build_account(assets: Vec<Asset>, nonce: Felt, slots: Vec<StorageSlot>) -> Account {
850 let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
851 let code = AccountCode::mock();
852
853 let vault = AssetVault::new(&assets).unwrap();
854
855 let storage = AccountStorage::new(slots).unwrap();
856
857 Account::new_existing(id, vault, storage, code, nonce)
858 }
859
860 #[test]
863 fn test_account_unsupported_component_type() {
864 let code1 = "export.foo add end";
865 let library1 = Assembler::default().assemble_library([code1]).unwrap();
866
867 let component1 = AccountComponent::new(library1, vec![])
869 .unwrap()
870 .with_supported_type(AccountType::FungibleFaucet)
871 .with_supported_type(AccountType::NonFungibleFaucet)
872 .with_supported_type(AccountType::RegularAccountImmutableCode);
873
874 let err = Account::initialize_from_components(
875 AccountType::RegularAccountUpdatableCode,
876 &[component1],
877 )
878 .unwrap_err();
879
880 assert!(matches!(
881 err,
882 AccountError::UnsupportedComponentForAccountType {
883 account_type: AccountType::RegularAccountUpdatableCode,
884 component_index: 0
885 }
886 ))
887 }
888
889 #[test]
892 fn test_account_duplicate_exported_mast_root() {
893 let code1 = "export.foo add eq.1 end";
894 let code2 = "export.bar add eq.1 end";
895
896 let library1 = Assembler::default().assemble_library([code1]).unwrap();
897 let library2 = Assembler::default().assemble_library([code2]).unwrap();
898
899 let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types();
900 let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types();
901
902 let err = Account::initialize_from_components(
903 AccountType::RegularAccountUpdatableCode,
904 &[NoopAuthComponent.into(), component1, component2],
905 )
906 .unwrap_err();
907
908 assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_))
909 }
910
911 #[test]
913 fn seed_validation() -> anyhow::Result<()> {
914 let account = AccountBuilder::new([5; 32])
915 .with_auth_component(NoopAuthComponent)
916 .with_component(AddComponent)
917 .build()?;
918 let (id, vault, storage, code, _nonce, seed) = account.into_parts();
919 assert!(seed.is_some());
920
921 let other_seed = AccountId::compute_account_seed(
922 [9; 32],
923 AccountType::FungibleFaucet,
924 Network,
925 AccountIdVersion::Version0,
926 code.commitment(),
927 storage.commitment(),
928 )?;
929
930 let err = Account::new(id, vault.clone(), storage.clone(), code.clone(), Felt::ONE, seed)
932 .unwrap_err();
933 assert_matches!(err, AccountError::ExistingAccountWithSeed);
934
935 let err = Account::new(id, vault.clone(), storage.clone(), code.clone(), Felt::ZERO, None)
937 .unwrap_err();
938 assert_matches!(err, AccountError::NewAccountMissingSeed);
939
940 let err = Account::new(
943 id,
944 vault.clone(),
945 storage.clone(),
946 code.clone(),
947 Felt::ZERO,
948 Some(other_seed),
949 )
950 .unwrap_err();
951 assert_matches!(err, AccountError::AccountIdSeedMismatch { .. });
952
953 let err = Account::new(
956 id,
957 vault.clone(),
958 storage.clone(),
959 code.clone(),
960 Felt::ZERO,
961 Some(Word::from([1, 2, 3, 4u32])),
962 )
963 .unwrap_err();
964 assert_matches!(err, AccountError::SeedConvertsToInvalidAccountId(_));
965
966 Account::new(id, vault.clone(), storage.clone(), code.clone(), Felt::ONE, None)?;
969
970 Account::new(id, vault.clone(), storage.clone(), code.clone(), Felt::ZERO, seed)?;
973
974 Ok(())
975 }
976
977 #[test]
978 fn incrementing_nonce_should_remove_seed() -> anyhow::Result<()> {
979 let mut account = AccountBuilder::new([5; 32])
980 .with_auth_component(NoopAuthComponent)
981 .with_component(AddComponent)
982 .build()?;
983 account.increment_nonce(Felt::ONE)?;
984
985 assert_matches!(account.seed(), None);
986
987 let _partial_account = PartialAccount::from(&account);
990
991 Ok(())
992 }
993}