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::NAME,
27 BasicWallet::RECEIVE_ASSET_PROC_NAME,
28 basic_wallet_library
29);
30
31procedure_digest!(
33 BASIC_WALLET_MOVE_ASSET_TO_NOTE,
34 BasicWallet::NAME,
35 BasicWallet::MOVE_ASSET_TO_NOTE_PROC_NAME,
36 basic_wallet_library
37);
38
39pub struct BasicWallet;
56
57impl BasicWallet {
58 pub const NAME: &'static str = "miden::standards::components::wallets::basic_wallet";
63
64 const RECEIVE_ASSET_PROC_NAME: &str = "receive_asset";
65 const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "move_asset_to_note";
66
67 pub fn receive_asset_digest() -> Word {
72 *BASIC_WALLET_RECEIVE_ASSET
73 }
74
75 pub fn move_asset_to_note_digest() -> Word {
77 *BASIC_WALLET_MOVE_ASSET_TO_NOTE
78 }
79
80 pub fn component_metadata() -> AccountComponentMetadata {
82 AccountComponentMetadata::new(Self::NAME, AccountType::all())
83 .with_description("Basic wallet component for receiving and sending assets")
84 }
85}
86
87impl From<BasicWallet> for AccountComponent {
88 fn from(_: BasicWallet) -> Self {
89 let metadata = BasicWallet::component_metadata();
90
91 AccountComponent::new(basic_wallet_library(), vec![], metadata).expect(
92 "basic wallet component should satisfy the requirements of a valid account component",
93 )
94 }
95}
96
97#[derive(Debug, Error)]
102pub enum BasicWalletError {
103 #[error("unsupported authentication method: {0}")]
104 UnsupportedAuthMethod(String),
105 #[error("account creation failed")]
106 AccountError(#[source] AccountError),
107}
108
109pub fn create_basic_wallet(
120 init_seed: [u8; 32],
121 auth_method: AuthMethod,
122 account_type: AccountType,
123 account_storage_mode: AccountStorageMode,
124) -> Result<Account, BasicWalletError> {
125 if matches!(account_type, AccountType::FungibleFaucet | AccountType::NonFungibleFaucet) {
126 return Err(BasicWalletError::AccountError(AccountError::other(
127 "basic wallet accounts cannot have a faucet account type",
128 )));
129 }
130
131 let auth_component: AccountComponent = match auth_method {
132 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => {
133 AuthSingleSig::new(pub_key, auth_scheme).into()
134 },
135 AuthMethod::Multisig { threshold, approvers } => {
136 let config = AuthMultisigConfig::new(approvers, threshold)
137 .and_then(|cfg| {
138 cfg.with_proc_thresholds(vec![(BasicWallet::receive_asset_digest(), 1)])
139 })
140 .map_err(BasicWalletError::AccountError)?;
141 AuthMultisig::new(config).map_err(BasicWalletError::AccountError)?.into()
142 },
143 AuthMethod::NoAuth => {
144 return Err(BasicWalletError::UnsupportedAuthMethod(
145 "basic wallets cannot be created with NoAuth authentication method".into(),
146 ));
147 },
148 AuthMethod::Unknown => {
149 return Err(BasicWalletError::UnsupportedAuthMethod(
150 "basic wallets cannot be created with Unknown authentication method".into(),
151 ));
152 },
153 };
154
155 let account = AccountBuilder::new(init_seed)
156 .account_type(account_type)
157 .storage_mode(account_storage_mode)
158 .with_auth_component(auth_component)
159 .with_component(BasicWallet)
160 .build()
161 .map_err(BasicWalletError::AccountError)?;
162
163 Ok(account)
164}
165
166#[cfg(test)]
170mod tests {
171 use miden_protocol::account::auth::{self, PublicKeyCommitment};
172 use miden_protocol::utils::serde::{Deserializable, Serializable};
173 use miden_protocol::{ONE, Word};
174
175 use super::{Account, AccountStorageMode, AccountType, AuthMethod, create_basic_wallet};
176 use crate::account::wallets::BasicWallet;
177
178 #[test]
179 fn test_create_basic_wallet() {
180 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
181 let auth_scheme = auth::AuthScheme::Falcon512Poseidon2;
182 let wallet = create_basic_wallet(
183 [1; 32],
184 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
185 AccountType::RegularAccountImmutableCode,
186 AccountStorageMode::Public,
187 );
188
189 wallet.unwrap_or_else(|err| {
190 panic!("{}", err);
191 });
192 }
193
194 #[test]
195 fn test_serialize_basic_wallet() {
196 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
197 let auth_scheme = auth::AuthScheme::EcdsaK256Keccak;
198 let wallet = create_basic_wallet(
199 [1; 32],
200 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
201 AccountType::RegularAccountImmutableCode,
202 AccountStorageMode::Public,
203 )
204 .unwrap();
205
206 let bytes = wallet.to_bytes();
207 let deserialized_wallet = Account::read_from_bytes(&bytes).unwrap();
208 assert_eq!(wallet, deserialized_wallet);
209 }
210
211 #[test]
213 fn get_faucet_procedures() {
214 let _receive_asset_digest = BasicWallet::receive_asset_digest();
215 let _move_asset_to_note_digest = BasicWallet::move_asset_to_note_digest();
216 }
217}