miden_standards/account/wallets/
mod.rs1use alloc::string::String;
2
3use miden_protocol::Word;
4use miden_protocol::account::component::AccountComponentMetadata;
5use miden_protocol::account::{
6 Account,
7 AccountBuilder,
8 AccountComponent,
9 AccountStorageMode,
10 AccountType,
11};
12use miden_protocol::errors::AccountError;
13use thiserror::Error;
14
15use super::AuthMethod;
16use crate::account::auth::{AuthMultisig, AuthMultisigConfig, AuthSingleSig};
17use crate::account::components::basic_wallet_library;
18use crate::procedure_digest;
19
20procedure_digest!(
25 BASIC_WALLET_RECEIVE_ASSET,
26 BasicWallet::RECEIVE_ASSET_PROC_NAME,
27 basic_wallet_library
28);
29
30procedure_digest!(
32 BASIC_WALLET_MOVE_ASSET_TO_NOTE,
33 BasicWallet::MOVE_ASSET_TO_NOTE_PROC_NAME,
34 basic_wallet_library
35);
36
37pub struct BasicWallet;
54
55impl BasicWallet {
56 pub const NAME: &'static str = "miden::basic_wallet";
61
62 const RECEIVE_ASSET_PROC_NAME: &str = "basic_wallet::receive_asset";
63 const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "basic_wallet::move_asset_to_note";
64
65 pub fn receive_asset_digest() -> Word {
70 *BASIC_WALLET_RECEIVE_ASSET
71 }
72
73 pub fn move_asset_to_note_digest() -> Word {
75 *BASIC_WALLET_MOVE_ASSET_TO_NOTE
76 }
77}
78
79impl From<BasicWallet> for AccountComponent {
80 fn from(_: BasicWallet) -> Self {
81 let metadata = AccountComponentMetadata::new(BasicWallet::NAME)
82 .with_description("Basic wallet component for receiving and sending assets")
83 .with_supports_all_types();
84
85 AccountComponent::new(basic_wallet_library(), vec![], metadata).expect(
86 "basic wallet component should satisfy the requirements of a valid account component",
87 )
88 }
89}
90
91#[derive(Debug, Error)]
96pub enum BasicWalletError {
97 #[error("unsupported authentication method: {0}")]
98 UnsupportedAuthMethod(String),
99 #[error("account creation failed")]
100 AccountError(#[source] AccountError),
101}
102
103pub fn create_basic_wallet(
114 init_seed: [u8; 32],
115 auth_method: AuthMethod,
116 account_type: AccountType,
117 account_storage_mode: AccountStorageMode,
118) -> Result<Account, BasicWalletError> {
119 if matches!(account_type, AccountType::FungibleFaucet | AccountType::NonFungibleFaucet) {
120 return Err(BasicWalletError::AccountError(AccountError::other(
121 "basic wallet accounts cannot have a faucet account type",
122 )));
123 }
124
125 let auth_component: AccountComponent = match auth_method {
126 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => {
127 AuthSingleSig::new(pub_key, auth_scheme).into()
128 },
129 AuthMethod::Multisig { threshold, approvers } => {
130 let config = AuthMultisigConfig::new(approvers, threshold)
131 .and_then(|cfg| {
132 cfg.with_proc_thresholds(vec![(BasicWallet::receive_asset_digest(), 1)])
133 })
134 .map_err(BasicWalletError::AccountError)?;
135 AuthMultisig::new(config).map_err(BasicWalletError::AccountError)?.into()
136 },
137 AuthMethod::NoAuth => {
138 return Err(BasicWalletError::UnsupportedAuthMethod(
139 "basic wallets cannot be created with NoAuth authentication method".into(),
140 ));
141 },
142 AuthMethod::Unknown => {
143 return Err(BasicWalletError::UnsupportedAuthMethod(
144 "basic wallets cannot be created with Unknown authentication method".into(),
145 ));
146 },
147 };
148
149 let account = AccountBuilder::new(init_seed)
150 .account_type(account_type)
151 .storage_mode(account_storage_mode)
152 .with_auth_component(auth_component)
153 .with_component(BasicWallet)
154 .build()
155 .map_err(BasicWalletError::AccountError)?;
156
157 Ok(account)
158}
159
160#[cfg(test)]
164mod tests {
165 use miden_processor::utils::{Deserializable, Serializable};
166 use miden_protocol::account::auth::{self, PublicKeyCommitment};
167 use miden_protocol::{ONE, Word};
168
169 use super::{Account, AccountStorageMode, AccountType, AuthMethod, create_basic_wallet};
170 use crate::account::wallets::BasicWallet;
171
172 #[test]
173 fn test_create_basic_wallet() {
174 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
175 let auth_scheme = auth::AuthScheme::Falcon512Rpo;
176 let wallet = create_basic_wallet(
177 [1; 32],
178 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
179 AccountType::RegularAccountImmutableCode,
180 AccountStorageMode::Public,
181 );
182
183 wallet.unwrap_or_else(|err| {
184 panic!("{}", err);
185 });
186 }
187
188 #[test]
189 fn test_serialize_basic_wallet() {
190 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
191 let auth_scheme = auth::AuthScheme::EcdsaK256Keccak;
192 let wallet = create_basic_wallet(
193 [1; 32],
194 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
195 AccountType::RegularAccountImmutableCode,
196 AccountStorageMode::Public,
197 )
198 .unwrap();
199
200 let bytes = wallet.to_bytes();
201 let deserialized_wallet = Account::read_from_bytes(&bytes).unwrap();
202 assert_eq!(wallet, deserialized_wallet);
203 }
204
205 #[test]
207 fn get_faucet_procedures() {
208 let _receive_asset_digest = BasicWallet::receive_asset_digest();
209 let _move_asset_to_note_digest = BasicWallet::move_asset_to_note_digest();
210 }
211}