use alloc::str::FromStr;
use miden_client::Felt as NativeFelt;
use miden_client::account::{AccountId as NativeAccountId, NetworkId as NativeNetworkId};
use miden_client::address::{
Address,
AddressId,
AddressInterface as NativeAccountInterface,
CustomNetworkId,
RoutingParameters,
};
use wasm_bindgen::prelude::*;
use super::felt::Felt;
use crate::js_error_with_context;
#[wasm_bindgen]
#[derive(Clone, Copy, Debug)]
pub struct AccountId(NativeAccountId);
#[wasm_bindgen]
pub enum NetworkType {
Mainnet = 0,
Testnet = 1,
Devnet = 2,
Custom = 3,
}
#[wasm_bindgen]
pub struct NetworkId {
network_type: NetworkType,
custom: Option<CustomNetworkId>,
}
#[wasm_bindgen]
impl NetworkId {
pub fn mainnet() -> NetworkId {
NetworkId {
network_type: NetworkType::Mainnet,
custom: None,
}
}
pub fn testnet() -> NetworkId {
NetworkId {
network_type: NetworkType::Testnet,
custom: None,
}
}
pub fn devnet() -> NetworkId {
NetworkId {
network_type: NetworkType::Devnet,
custom: None,
}
}
pub fn custom(custom_prefix: &str) -> Result<NetworkId, JsValue> {
let custom = CustomNetworkId::from_str(custom_prefix)
.map_err(|err| js_error_with_context(err, "Error building custom id prefix"))?;
Ok(NetworkId {
network_type: NetworkType::Custom,
custom: Some(custom),
})
}
}
#[wasm_bindgen]
#[repr(u8)]
pub enum AccountInterface {
BasicWallet = 0,
}
#[wasm_bindgen]
impl AccountId {
#[wasm_bindgen(js_name = "fromHex")]
pub fn from_hex(hex: &str) -> Result<AccountId, JsValue> {
let native_account_id = NativeAccountId::from_hex(hex)
.map_err(|err| js_error_with_context(err, "error instantiating AccountId from hex"))?;
Ok(AccountId(native_account_id))
}
#[wasm_bindgen(js_name = "fromPrefixSuffix")]
pub fn from_prefix_suffix(prefix: &Felt, suffix: &Felt) -> Result<AccountId, JsValue> {
let prefix_felt: NativeFelt = (*prefix).into();
let suffix_felt: NativeFelt = (*suffix).into();
let native_account_id = NativeAccountId::try_from_elements(suffix_felt, prefix_felt)
.map_err(|err| {
js_error_with_context(err, "error instantiating AccountId from prefix and suffix")
})?;
Ok(AccountId(native_account_id))
}
#[wasm_bindgen(js_name = "isFaucet")]
pub fn is_faucet(&self) -> bool {
self.0.is_faucet()
}
#[wasm_bindgen(js_name = "isRegularAccount")]
pub fn is_regular_account(&self) -> bool {
self.0.is_regular_account()
}
#[wasm_bindgen(js_name = "isPublic")]
pub fn is_public(&self) -> bool {
self.0.is_public()
}
#[wasm_bindgen(js_name = "isPrivate")]
pub fn is_private(&self) -> bool {
self.0.is_private()
}
#[wasm_bindgen(js_name = "isNetwork")]
pub fn is_network(&self) -> bool {
self.0.is_network()
}
#[wasm_bindgen(js_name = "toString")]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
self.0.to_string()
}
#[wasm_bindgen(js_name = "toBech32")]
pub fn to_bech32(
&self,
network_id: NetworkId,
account_interface: AccountInterface,
) -> Result<String, JsValue> {
let network_id: NativeNetworkId = network_id.into();
let routing_params = RoutingParameters::new(account_interface.into());
let address = Address::new(self.0).with_routing_parameters(routing_params);
Ok(address.encode(network_id))
}
#[wasm_bindgen(js_name = "fromBech32")]
pub fn from_bech32(bech_32_encoded_id: &str) -> Result<AccountId, JsValue> {
let (_, address) = Address::decode(bech_32_encoded_id).map_err(|err| {
js_error_with_context(err, "could not interpret input as a bech32-encoded account id")
})?;
match address.id() {
AddressId::AccountId(account_id) => Ok(account_id.into()),
_unsupported => {
Err(JsValue::from_str("bech32 string decoded into an unsupported address kind"))
},
}
}
pub fn prefix(&self) -> Felt {
let native_felt: NativeFelt = self.0.prefix().as_felt();
native_felt.into()
}
pub fn suffix(&self) -> Felt {
let native_felt: NativeFelt = self.0.suffix();
native_felt.into()
}
pub(crate) fn as_native(&self) -> &NativeAccountId {
&self.0
}
}
impl From<NativeAccountId> for AccountId {
fn from(native_account_id: NativeAccountId) -> Self {
AccountId(native_account_id)
}
}
impl From<&NativeAccountId> for AccountId {
fn from(native_account_id: &NativeAccountId) -> Self {
AccountId(*native_account_id)
}
}
impl From<AccountId> for NativeAccountId {
fn from(account_id: AccountId) -> Self {
account_id.0
}
}
impl From<&AccountId> for NativeAccountId {
fn from(account_id: &AccountId) -> Self {
account_id.0
}
}
impl From<NetworkId> for NativeNetworkId {
fn from(value: NetworkId) -> Self {
match value.network_type {
NetworkType::Mainnet => NativeNetworkId::Mainnet,
NetworkType::Testnet => NativeNetworkId::Testnet,
NetworkType::Devnet => NativeNetworkId::Devnet,
NetworkType::Custom => {
let custom_prefix =
value.custom.expect("custom network id constructor implies existing prefix");
NativeNetworkId::from_str(custom_prefix.as_str())
.expect("custom network id constructor implies valid prefix")
},
}
}
}
impl From<AccountInterface> for NativeAccountInterface {
fn from(account_interface: AccountInterface) -> Self {
match account_interface {
AccountInterface::BasicWallet => NativeAccountInterface::BasicWallet,
}
}
}