miden_standards/account/wallets/
mod.rs1use alloc::string::String;
2
3use miden_protocol::account::component::{AccountComponentCode, AccountComponentMetadata};
4use miden_protocol::account::{
5 Account,
6 AccountBuilder,
7 AccountComponent,
8 AccountComponentName,
9 AccountProcedureRoot,
10 AccountType,
11};
12use miden_protocol::errors::AccountError;
13use thiserror::Error;
14
15use super::AuthMethod;
16use crate::account::account_component_code;
17use crate::account::auth::{AuthMultisig, AuthMultisigConfig, AuthSingleSig};
18use crate::procedure_root;
19
20account_component_code!(BASIC_WALLET_CODE, "wallets/basic_wallet.masl");
24
25procedure_root!(
27 BASIC_WALLET_RECEIVE_ASSET,
28 BasicWallet::NAME,
29 BasicWallet::RECEIVE_ASSET_PROC_NAME,
30 BasicWallet::code()
31);
32
33procedure_root!(
36 BASIC_WALLET_MOVE_ASSET_TO_NOTE,
37 BasicWallet::NAME,
38 BasicWallet::MOVE_ASSET_TO_NOTE_PROC_NAME,
39 BasicWallet::code()
40);
41
42pub struct BasicWallet;
57
58impl BasicWallet {
59 pub const NAME: &'static str = "miden::standards::components::wallets::basic_wallet";
64
65 const RECEIVE_ASSET_PROC_NAME: &str = "receive_asset";
66 const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "move_asset_to_note";
67
68 pub const fn name() -> AccountComponentName {
70 AccountComponentName::from_static_str(Self::NAME)
71 }
72
73 pub fn code() -> &'static AccountComponentCode {
78 &BASIC_WALLET_CODE
79 }
80
81 pub fn receive_asset_root() -> AccountProcedureRoot {
83 *BASIC_WALLET_RECEIVE_ASSET
84 }
85
86 pub fn move_asset_to_note_root() -> AccountProcedureRoot {
88 *BASIC_WALLET_MOVE_ASSET_TO_NOTE
89 }
90
91 pub fn component_metadata() -> AccountComponentMetadata {
93 AccountComponentMetadata::new(Self::NAME)
94 .with_description("Basic wallet component for receiving and sending assets")
95 }
96}
97
98impl From<BasicWallet> for AccountComponent {
99 fn from(_: BasicWallet) -> Self {
100 let metadata = BasicWallet::component_metadata();
101
102 AccountComponent::new(BasicWallet::code().clone(), vec![], metadata).expect(
103 "basic wallet component should satisfy the requirements of a valid account component",
104 )
105 }
106}
107
108#[derive(Debug, Error)]
113pub enum BasicWalletError {
114 #[error("unsupported authentication method: {0}")]
115 UnsupportedAuthMethod(String),
116 #[error("account creation failed")]
117 AccountError(#[source] AccountError),
118}
119
120pub fn create_basic_wallet(
131 init_seed: [u8; 32],
132 auth_method: AuthMethod,
133 account_storage_mode: AccountType,
134) -> Result<Account, BasicWalletError> {
135 let auth_component: AccountComponent = match auth_method {
136 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => {
137 AuthSingleSig::new(pub_key, auth_scheme).into()
138 },
139 AuthMethod::Multisig { threshold, approvers } => {
140 let config = AuthMultisigConfig::new(approvers, threshold)
141 .and_then(|cfg| {
142 cfg.with_proc_thresholds(vec![(BasicWallet::receive_asset_root(), 1)])
143 })
144 .map_err(BasicWalletError::AccountError)?;
145 AuthMultisig::new(config).map_err(BasicWalletError::AccountError)?.into()
146 },
147 AuthMethod::NoAuth => {
148 return Err(BasicWalletError::UnsupportedAuthMethod(
149 "basic wallets cannot be created with NoAuth authentication method".into(),
150 ));
151 },
152 AuthMethod::NetworkAccount { .. } => {
153 return Err(BasicWalletError::UnsupportedAuthMethod(
154 "basic wallets cannot be created with NetworkAccount authentication method".into(),
155 ));
156 },
157 AuthMethod::Unknown => {
158 return Err(BasicWalletError::UnsupportedAuthMethod(
159 "basic wallets cannot be created with Unknown authentication method".into(),
160 ));
161 },
162 };
163
164 let account = AccountBuilder::new(init_seed)
165 .account_type(account_storage_mode)
166 .with_auth_component(auth_component)
167 .with_component(BasicWallet)
168 .build()
169 .map_err(BasicWalletError::AccountError)?;
170
171 Ok(account)
172}
173
174#[cfg(test)]
178mod tests {
179 use miden_protocol::account::auth::{self, PublicKeyCommitment};
180 use miden_protocol::utils::serde::{Deserializable, Serializable};
181 use miden_protocol::{ONE, Word};
182
183 use super::{Account, AccountType, AuthMethod, create_basic_wallet};
184 use crate::account::wallets::BasicWallet;
185
186 #[test]
187 fn test_create_basic_wallet() {
188 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
189 let auth_scheme = auth::AuthScheme::Falcon512Poseidon2;
190 let wallet = create_basic_wallet(
191 [1; 32],
192 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
193 AccountType::Public,
194 );
195
196 wallet.unwrap_or_else(|err| {
197 panic!("{}", err);
198 });
199 }
200
201 #[test]
202 fn test_serialize_basic_wallet() {
203 let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
204 let auth_scheme = auth::AuthScheme::EcdsaK256Keccak;
205 let wallet = create_basic_wallet(
206 [1; 32],
207 AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
208 AccountType::Public,
209 )
210 .unwrap();
211
212 let bytes = wallet.to_bytes();
213 let deserialized_wallet = Account::read_from_bytes(&bytes).unwrap();
214 assert_eq!(wallet, deserialized_wallet);
215 }
216
217 #[test]
219 fn get_faucet_procedures() {
220 let _receive_asset_root = BasicWallet::receive_asset_root();
221 let _move_asset_to_note_root = BasicWallet::move_asset_to_note_root();
222 }
223}