use miden_client::Word as NativeWord;
use miden_client::account::component::{
AccountComponent as NativeAccountComponent,
AccountComponentMetadata,
};
use miden_client::account::{
AccountComponentCode as NativeAccountComponentCode,
AccountType,
StorageSlot as NativeStorageSlot,
};
use miden_client::assembly::{Library as NativeLibrary, MastNodeExt};
use miden_client::auth::{
AuthSchemeId as NativeAuthSchemeId,
AuthSecretKey as NativeSecretKey,
AuthSingleSig as NativeSingleSig,
PublicKeyCommitment,
};
use miden_client::vm::Package as NativePackage;
use wasm_bindgen::prelude::*;
use crate::js_error_with_context;
use crate::models::account_component_code::AccountComponentCode;
use crate::models::auth::AuthScheme;
use crate::models::auth_secret_key::AuthSecretKey;
use crate::models::library::Library;
use crate::models::miden_arrays::StorageSlotArray;
use crate::models::package::Package;
use crate::models::storage_slot::StorageSlot;
use crate::models::word::Word;
#[derive(Clone)]
#[wasm_bindgen]
pub struct GetProceduresResultItem {
digest: Word,
is_auth: bool,
}
#[wasm_bindgen]
impl GetProceduresResultItem {
#[wasm_bindgen(getter)]
pub fn digest(&self) -> Word {
self.digest.clone()
}
#[wasm_bindgen(getter, js_name = "isAuth")]
pub fn is_auth(&self) -> bool {
self.is_auth
}
}
impl From<(miden_protocol::account::AccountProcedureRoot, bool)> for GetProceduresResultItem {
fn from(
native_get_procedures_result_item: (miden_protocol::account::AccountProcedureRoot, bool),
) -> Self {
let digest_word: NativeWord = native_get_procedures_result_item.0.into();
Self {
digest: digest_word.into(),
is_auth: native_get_procedures_result_item.1,
}
}
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct AccountComponent(NativeAccountComponent);
#[wasm_bindgen]
impl AccountComponent {
pub fn compile(
account_code: AccountComponentCode,
storage_slots: Vec<StorageSlot>,
) -> Result<AccountComponent, JsValue> {
let native_slots: Vec<NativeStorageSlot> =
storage_slots.into_iter().map(Into::into).collect();
let native_account_code: NativeAccountComponentCode = account_code.into();
NativeAccountComponent::new(
native_account_code,
native_slots,
AccountComponentMetadata::new("custom", AccountType::all()),
)
.map(AccountComponent)
.map_err(|e| js_error_with_context(e, "Failed to compile account component"))
}
#[wasm_bindgen(js_name = "withSupportsAllTypes")]
pub fn with_supports_all_types(self) -> Self {
let code = self.0.component_code().clone();
let slots = self.0.storage_slots().to_vec();
let name = self.0.metadata().name();
let metadata = AccountComponentMetadata::new(name, AccountType::all());
AccountComponent(
NativeAccountComponent::new(code, slots, metadata)
.expect("reconstructing component with updated metadata should not fail"),
)
}
#[wasm_bindgen(js_name = "getProcedureHash")]
pub fn get_procedure_hash(&self, procedure_name: &str) -> Result<String, JsValue> {
let library = self.0.component_code().as_library();
let get_proc_export = library
.exports()
.find(|export| {
if export.as_procedure().is_none() {
return false;
}
let export_path = export.path();
let path_str = export_path.as_ref().as_str();
path_str == procedure_name
|| export_path.as_ref().to_relative().as_str() == procedure_name
|| path_str.rsplit_once("::").is_some_and(|(_, local)| local == procedure_name)
})
.ok_or_else(|| {
JsValue::from_str(&format!(
"Procedure {procedure_name} not found in the account component library"
))
})?;
let get_proc_mast_id = library.get_export_node_id(get_proc_export.path());
let digest_hex = library
.mast_forest()
.get_node_by_id(get_proc_mast_id)
.ok_or_else(|| {
JsValue::from_str(&format!("Mast node for procedure {procedure_name} not found"))
})?
.digest()
.to_hex();
Ok(digest_hex)
}
#[wasm_bindgen(js_name = "getProcedures")]
pub fn get_procedures(&self) -> Vec<GetProceduresResultItem> {
self.0.procedures().map(Into::into).collect()
}
fn create_auth_component(
commitment: PublicKeyCommitment,
auth_scheme: AuthScheme,
) -> AccountComponent {
match auth_scheme {
AuthScheme::AuthRpoFalcon512 => {
let auth = NativeSingleSig::new(commitment, NativeAuthSchemeId::Falcon512Poseidon2);
AccountComponent(auth.into())
},
AuthScheme::AuthEcdsaK256Keccak => {
let auth = NativeSingleSig::new(commitment, NativeAuthSchemeId::EcdsaK256Keccak);
AccountComponent(auth.into())
},
}
}
#[wasm_bindgen(js_name = "createAuthComponentFromSecretKey")]
pub fn create_auth_component_from_secret_key(
secret_key: &AuthSecretKey,
) -> Result<AccountComponent, JsValue> {
let native_secret_key: NativeSecretKey = secret_key.into();
let commitment = native_secret_key.public_key().to_commitment();
let auth_scheme = match native_secret_key {
NativeSecretKey::EcdsaK256Keccak(_) => AuthScheme::AuthEcdsaK256Keccak,
NativeSecretKey::Falcon512Poseidon2(_) => AuthScheme::AuthRpoFalcon512,
_unimplemented => {
return Err(JsValue::from_str(
"building auth component for this auth scheme is not supported yet",
));
},
};
Ok(AccountComponent::create_auth_component(commitment, auth_scheme))
}
#[wasm_bindgen(js_name = "createAuthComponentFromCommitment")]
pub fn create_auth_component_from_commitment(
commitment: &Word,
auth_scheme: AuthScheme,
) -> Result<AccountComponent, JsValue> {
let native_word: NativeWord = commitment.into();
let pkc = PublicKeyCommitment::from(native_word);
Ok(AccountComponent::create_auth_component(pkc, auth_scheme))
}
#[wasm_bindgen(js_name = "fromPackage")]
pub fn from_package(
package: &Package,
storage_slots: &StorageSlotArray,
) -> Result<AccountComponent, JsValue> {
let native_package: NativePackage = package.into();
let native_library = (*native_package.mast).clone();
let native_slots: Vec<NativeStorageSlot> = storage_slots
.__inner
.iter()
.map(|storage_slot| storage_slot.clone().into())
.collect();
NativeAccountComponent::new(
native_library,
native_slots,
AccountComponentMetadata::new("custom", AccountType::all()),
)
.map(AccountComponent)
.map_err(|e| js_error_with_context(e, "Failed to create account component from package"))
}
#[wasm_bindgen(js_name = "fromLibrary")]
pub fn from_library(
library: &Library,
storage_slots: Vec<StorageSlot>,
) -> Result<AccountComponent, JsValue> {
let native_library: NativeLibrary = library.into();
let native_slots: Vec<NativeStorageSlot> =
storage_slots.into_iter().map(Into::into).collect();
NativeAccountComponent::new(
native_library,
native_slots,
AccountComponentMetadata::new("custom", AccountType::all()),
)
.map(AccountComponent)
.map_err(|e| js_error_with_context(e, "Failed to create account component from library"))
}
}
impl From<AccountComponent> for NativeAccountComponent {
fn from(account_component: AccountComponent) -> Self {
account_component.0
}
}
impl From<NativeAccountComponent> for AccountComponent {
fn from(native_account_component: NativeAccountComponent) -> Self {
AccountComponent(native_account_component)
}
}
impl From<&AccountComponent> for NativeAccountComponent {
fn from(account_component: &AccountComponent) -> Self {
account_component.0.clone()
}
}