|
| 1 | +use crate::wasm_transport::WasmHttpTransport; |
| 2 | +use alloy::primitives::Address; |
| 3 | +use alloy::providers::ProviderBuilder; |
| 4 | +use alloy_rpc_client::RpcClient; |
| 5 | +use wasm_bindgen::prelude::*; |
| 6 | +use wasm_bindgen_futures::future_to_promise; |
| 7 | +use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::guardian::list::get_guardians_list; |
| 8 | + |
| 9 | +/// Get the list of guardians for a smart account |
| 10 | +/// |
| 11 | +/// # Parameters |
| 12 | +/// * `rpc_url` - RPC URL for the blockchain network |
| 13 | +/// * `account_address` - Address of the smart account |
| 14 | +/// * `guardian_executor_address` - Address of the GuardianExecutor contract |
| 15 | +/// |
| 16 | +/// # Returns |
| 17 | +/// Promise that resolves to a JSON array of guardian addresses (hex strings) |
| 18 | +#[wasm_bindgen] |
| 19 | +pub fn get_guardians_list_wasm( |
| 20 | + rpc_url: String, |
| 21 | + account_address: String, |
| 22 | + guardian_executor_address: String, |
| 23 | +) -> js_sys::Promise { |
| 24 | + future_to_promise(async move { |
| 25 | + // Parse addresses |
| 26 | + let account_addr = match account_address.parse::<Address>() { |
| 27 | + Ok(addr) => addr, |
| 28 | + Err(e) => { |
| 29 | + return Err(JsValue::from_str(&format!( |
| 30 | + "Invalid account address: {}", |
| 31 | + e |
| 32 | + ))); |
| 33 | + } |
| 34 | + }; |
| 35 | + |
| 36 | + let guardian_executor_addr = |
| 37 | + match guardian_executor_address.parse::<Address>() { |
| 38 | + Ok(addr) => addr, |
| 39 | + Err(e) => { |
| 40 | + return Err(JsValue::from_str(&format!( |
| 41 | + "Invalid guardian executor address: {}", |
| 42 | + e |
| 43 | + ))); |
| 44 | + } |
| 45 | + }; |
| 46 | + |
| 47 | + // Create transport and provider |
| 48 | + let transport = WasmHttpTransport::new(rpc_url); |
| 49 | + let client = RpcClient::new(transport.clone(), false); |
| 50 | + let provider = ProviderBuilder::new().connect_client(client); |
| 51 | + |
| 52 | + // Call the core function |
| 53 | + match get_guardians_list(account_addr, guardian_executor_addr, provider) |
| 54 | + .await |
| 55 | + { |
| 56 | + Ok(guardians) => { |
| 57 | + let addresses: Vec<String> = guardians |
| 58 | + .iter() |
| 59 | + .map(|addr| format!("0x{:x}", addr)) |
| 60 | + .collect(); |
| 61 | + let json = serde_json::to_string(&addresses).map_err(|e| { |
| 62 | + JsValue::from_str(&format!( |
| 63 | + "Failed to serialize guardians list: {}", |
| 64 | + e |
| 65 | + )) |
| 66 | + })?; |
| 67 | + Ok(JsValue::from_str(&json)) |
| 68 | + } |
| 69 | + Err(e) => Err(JsValue::from_str(&format!( |
| 70 | + "Failed to get guardians list: {}", |
| 71 | + e |
| 72 | + ))), |
| 73 | + } |
| 74 | + }) |
| 75 | +} |
| 76 | + |
| 77 | +#[cfg(test)] |
| 78 | +mod tests { |
| 79 | + use super::*; |
| 80 | + use alloy::primitives::address; |
| 81 | + use serde_json; |
| 82 | + use wasm_bindgen_futures::JsFuture; |
| 83 | + use zksync_sso_erc4337_core::{ |
| 84 | + erc4337::{ |
| 85 | + account::{ |
| 86 | + erc7579::module::add::{ |
| 87 | + AddModuleParams, AddModulePayload, add_module, |
| 88 | + }, |
| 89 | + modular_smart_account::{ |
| 90 | + deploy::{DeployAccountParams, EOASigners, deploy_account}, |
| 91 | + guardian::propose::{ |
| 92 | + ProposeGuardianParams, propose_guardian, |
| 93 | + }, |
| 94 | + test_utilities::fund_account_with_default_amount, |
| 95 | + }, |
| 96 | + }, |
| 97 | + signer::create_eoa_signer, |
| 98 | + }, |
| 99 | + utils::alloy_utilities::test_utilities::{ |
| 100 | + TestInfraConfig, |
| 101 | + start_anvil_and_deploy_contracts_and_start_bundler_with_config, |
| 102 | + }, |
| 103 | + }; |
| 104 | + |
| 105 | + #[tokio::test] |
| 106 | + #[cfg(not(target_arch = "wasm32"))] |
| 107 | + async fn test_get_guardians_list_wasm() -> eyre::Result<()> { |
| 108 | + let ( |
| 109 | + _node_url, |
| 110 | + anvil_instance, |
| 111 | + provider, |
| 112 | + contracts, |
| 113 | + signer_private_key, |
| 114 | + bundler, |
| 115 | + bundler_client, |
| 116 | + ) = { |
| 117 | + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); |
| 118 | + let config = TestInfraConfig { |
| 119 | + signer_private_key: signer_private_key.clone(), |
| 120 | + }; |
| 121 | + start_anvil_and_deploy_contracts_and_start_bundler_with_config( |
| 122 | + &config, |
| 123 | + ) |
| 124 | + .await? |
| 125 | + }; |
| 126 | + |
| 127 | + let entry_point_address = |
| 128 | + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); |
| 129 | + |
| 130 | + let factory_address = contracts.account_factory; |
| 131 | + let eoa_validator_address = contracts.eoa_validator; |
| 132 | + |
| 133 | + let signer_address = |
| 134 | + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); |
| 135 | + let signers = vec![signer_address]; |
| 136 | + |
| 137 | + let eoa_signers = EOASigners { |
| 138 | + addresses: signers, |
| 139 | + validator_address: eoa_validator_address, |
| 140 | + }; |
| 141 | + |
| 142 | + let account_address = deploy_account(DeployAccountParams { |
| 143 | + factory_address, |
| 144 | + eoa_signers: Some(eoa_signers), |
| 145 | + webauthn_signer: None, |
| 146 | + session_validator: None, |
| 147 | + id: None, |
| 148 | + provider: provider.clone(), |
| 149 | + }) |
| 150 | + .await?; |
| 151 | + |
| 152 | + println!("Account deployed"); |
| 153 | + |
| 154 | + fund_account_with_default_amount(account_address, provider.clone()) |
| 155 | + .await?; |
| 156 | + |
| 157 | + let signer = create_eoa_signer( |
| 158 | + signer_private_key.clone(), |
| 159 | + eoa_validator_address, |
| 160 | + )?; |
| 161 | + |
| 162 | + // Install WebAuthn module |
| 163 | + { |
| 164 | + let webauthn_module = contracts.webauthn_validator; |
| 165 | + add_module(AddModuleParams { |
| 166 | + account_address, |
| 167 | + module: AddModulePayload::webauthn(webauthn_module), |
| 168 | + entry_point_address, |
| 169 | + paymaster: None, |
| 170 | + provider: provider.clone(), |
| 171 | + bundler_client: bundler_client.clone(), |
| 172 | + signer: signer.clone(), |
| 173 | + }) |
| 174 | + .await?; |
| 175 | + } |
| 176 | + |
| 177 | + let guardian_module = contracts.guardian_executor; |
| 178 | + |
| 179 | + // Install Guardian module |
| 180 | + add_module(AddModuleParams { |
| 181 | + account_address, |
| 182 | + module: AddModulePayload::guardian(guardian_module), |
| 183 | + entry_point_address, |
| 184 | + paymaster: None, |
| 185 | + provider: provider.clone(), |
| 186 | + bundler_client: bundler_client.clone(), |
| 187 | + signer: signer.clone(), |
| 188 | + }) |
| 189 | + .await?; |
| 190 | + |
| 191 | + // Use a different address for the guardian (not the account owner) |
| 192 | + let new_guardian = |
| 193 | + address!("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"); |
| 194 | + |
| 195 | + // Propose guardian using core function |
| 196 | + propose_guardian(ProposeGuardianParams { |
| 197 | + guardian_executor: guardian_module, |
| 198 | + new_guardian, |
| 199 | + account: account_address, |
| 200 | + entry_point: entry_point_address, |
| 201 | + paymaster: None, |
| 202 | + provider: provider.clone(), |
| 203 | + bundler_client: bundler_client.clone(), |
| 204 | + signer: signer.clone(), |
| 205 | + }) |
| 206 | + .await?; |
| 207 | + |
| 208 | + // Get guardians list using WASM wrapper |
| 209 | + let promise = get_guardians_list_wasm( |
| 210 | + "http://127.0.0.1:8545".to_string(), // Use default anvil URL |
| 211 | + format!("0x{:x}", account_address), |
| 212 | + format!("0x{:x}", guardian_module), |
| 213 | + ); |
| 214 | + |
| 215 | + let result = JsFuture::from(promise) |
| 216 | + .await |
| 217 | + .map_err(|e| eyre::eyre!("Failed to await promise: {:?}", e))?; |
| 218 | + let json_str = |
| 219 | + result.as_string().ok_or(eyre::eyre!("Expected string result"))?; |
| 220 | + let guardians: Vec<String> = serde_json::from_str(&json_str)?; |
| 221 | + |
| 222 | + eyre::ensure!( |
| 223 | + guardians.len() == 1, |
| 224 | + "Expected exactly one guardian in the list" |
| 225 | + ); |
| 226 | + eyre::ensure!( |
| 227 | + guardians[0].to_lowercase() |
| 228 | + == format!("0x{:x}", new_guardian).to_lowercase(), |
| 229 | + "Guardian address mismatch" |
| 230 | + ); |
| 231 | + |
| 232 | + drop(anvil_instance); |
| 233 | + drop(bundler); |
| 234 | + |
| 235 | + Ok(()) |
| 236 | + } |
| 237 | +} |
0 commit comments