use miden_client::account::AccountBuilder as NativeAccountBuilder;
use miden_client::account::component::BasicWallet;
use miden_client::auth::NoAuth;
use wasm_bindgen::prelude::*;
use crate::js_error_with_context;
use crate::models::account::Account;
use crate::models::account_component::AccountComponent;
use crate::models::account_storage_mode::AccountStorageMode;
use crate::models::account_type::AccountType;
use crate::models::word::Word;
#[wasm_bindgen]
pub struct AccountBuilderResult {
account: Account,
seed: Word,
}
#[wasm_bindgen]
impl AccountBuilderResult {
#[wasm_bindgen(getter)]
pub fn account(&self) -> Account {
self.account.clone()
}
#[wasm_bindgen(getter)]
pub fn seed(&self) -> Word {
self.seed.clone()
}
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct AccountBuilder(NativeAccountBuilder);
#[wasm_bindgen]
impl AccountBuilder {
#[wasm_bindgen(constructor)]
pub fn new(init_seed: Vec<u8>) -> Result<AccountBuilder, JsValue> {
let seed_array: [u8; 32] = init_seed
.try_into()
.map_err(|_| JsValue::from_str("Seed must be exactly 32 bytes"))?;
Ok(AccountBuilder(NativeAccountBuilder::new(seed_array)))
}
#[wasm_bindgen(js_name = "accountType")]
pub fn account_type(mut self, account_type: &JsValue) -> Result<AccountBuilder, JsValue> {
let at = parse_account_type(account_type)?;
self.0 = self.0.account_type(at.into());
Ok(self)
}
#[wasm_bindgen(js_name = "storageMode")]
pub fn storage_mode(mut self, storage_mode: &AccountStorageMode) -> Self {
self.0 = self.0.storage_mode(storage_mode.into());
self
}
#[wasm_bindgen(js_name = "withComponent")]
pub fn with_component(mut self, account_component: &AccountComponent) -> Self {
self.0 = self.0.with_component(account_component);
self
}
#[wasm_bindgen(js_name = "withAuthComponent")]
pub fn with_auth_component(mut self, account_component: &AccountComponent) -> Self {
self.0 = self.0.with_auth_component(account_component);
self
}
#[wasm_bindgen(js_name = "withNoAuthComponent")]
pub fn with_no_auth_component(mut self) -> Self {
self.0 = self.0.with_auth_component(NoAuth);
self
}
#[wasm_bindgen(js_name = "withBasicWalletComponent")]
pub fn with_basic_wallet_component(mut self) -> Self {
self.0 = self.0.with_component(BasicWallet);
self
}
pub fn build(self) -> Result<AccountBuilderResult, JsValue> {
let account = self
.0
.build()
.map_err(|err| js_error_with_context(err, "Failed to build account"))?;
let seed = account.seed().expect("newly built account should always contain a seed");
Ok(AccountBuilderResult {
account: account.into(),
seed: seed.into(),
})
}
}
fn parse_account_type(value: &JsValue) -> Result<AccountType, JsValue> {
if let Some(n) = value.as_f64() {
match account_type_from_f64(n) {
Some(at) => Ok(at),
None => Err(JsValue::from_str(&format!(
"Unknown account type: {n}. Expected 0 (FungibleFaucet), \
1 (NonFungibleFaucet), 2 (RegularAccountImmutableCode), \
or 3 (RegularAccountUpdatableCode)"
))),
}
} else if let Some(s) = value.as_string() {
match s.as_str() {
"FungibleFaucet" => Ok(AccountType::FungibleFaucet),
"NonFungibleFaucet" => Ok(AccountType::NonFungibleFaucet),
"RegularAccountImmutableCode" => Ok(AccountType::RegularAccountImmutableCode),
"RegularAccountUpdatableCode" => Ok(AccountType::RegularAccountUpdatableCode),
_ => Err(JsValue::from_str(&format!(
"Unknown account type: \"{s}\". Expected \"FungibleFaucet\", \
\"NonFungibleFaucet\", \"RegularAccountImmutableCode\", \
or \"RegularAccountUpdatableCode\""
))),
}
} else {
Err(JsValue::from_str("accountType must be a number (0–3) or a string variant name"))
}
}
#[allow(clippy::float_cmp)]
fn account_type_from_f64(n: f64) -> Option<AccountType> {
if n == 0.0 {
Some(AccountType::FungibleFaucet)
} else if n == 1.0 {
Some(AccountType::NonFungibleFaucet)
} else if n == 2.0 {
Some(AccountType::RegularAccountImmutableCode)
} else if n == 3.0 {
Some(AccountType::RegularAccountUpdatableCode)
} else {
None
}
}