Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ use alloy::{

#[derive(Clone)]
pub struct RemoveGuardianParams<P: Provider + Send + Sync + Clone> {
guardian_executor: Address,
guardian_to_remove: Address,
account_address: Address,
entry_point_address: Address,
paymaster: Option<PaymasterParams>,
provider: P,
bundler_client: BundlerClient,
signer: Signer,
pub guardian_executor: Address,
pub guardian_to_remove: Address,
pub account_address: Address,
pub entry_point_address: Address,
pub paymaster: Option<PaymasterParams>,
pub provider: P,
pub bundler_client: BundlerClient,
pub signer: Signer,
}

pub async fn remove_guardian<P>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ pub mod signature_wasm;
pub mod state;
pub mod status;

pub mod contract;
pub(crate) mod contract;
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ impl AltoTestHelper {
use crate::erc4337::bundler::{
config::BundlerConfig, pimlico::client::BundlerClient,
};
assert!(self.port != 4337);
let bundler_url = format!("http://127.0.0.1:{}", self.port);
assert!(!bundler_url.contains(":4337"));
let config = BundlerConfig::new(bundler_url);
BundlerClient::new(config)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod modular_smart_account;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod guardian;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod accept;
pub mod list;
pub mod propose;
pub mod recovery;
pub mod remove;
pub mod status;
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::wasm_transport::WasmHttpTransport;
use alloy::{
network::EthereumWallet,
primitives::Address,
providers::ProviderBuilder,
signers::local::PrivateKeySigner,
};
use alloy_rpc_client::RpcClient;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::guardian::accept::{
accept_guardian, AcceptGuardianParams,
};

/// Accept a proposed guardian for a smart account
///
/// This function allows a guardian to accept their proposed role for a smart account.
/// The guardian must sign the transaction using their private key.
///
/// # Parameters
/// * `rpc_url` - RPC URL for the blockchain network
/// * `guardian_executor` - Address of the GuardianExecutor contract
/// * `account` - Address of the smart account
/// * `guardian_private_key` - Private key of the guardian (0x-prefixed hex string)
///
/// # Returns
/// Promise that resolves to the transaction receipt hash as a hex string
#[wasm_bindgen]
pub fn accept_guardian_wasm(
rpc_url: String,
guardian_executor: String,
account: String,
guardian_private_key: String,
) -> js_sys::Promise {
future_to_promise(async move {
// Parse addresses
let guardian_executor_addr = match guardian_executor.parse::<Address>()
{
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid guardian executor address: {}",
e
)));
}
};

let account_addr = match account.parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid account address: {}",
e
)));
}
};

// Parse guardian private key
let guardian_key = match guardian_private_key
.trim_start_matches("0x")
.parse::<PrivateKeySigner>()
{
Ok(signer) => signer,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid guardian private key: {}",
e
)));
}
};

let guardian_wallet = EthereumWallet::from(guardian_key);

// Create transport and provider with wallet
let transport = WasmHttpTransport::new(rpc_url);
let client = RpcClient::new(transport.clone(), false);
let guardian_provider = ProviderBuilder::new()
.wallet(guardian_wallet)
.connect_client(client);

// Call the core function
match accept_guardian(AcceptGuardianParams {
guardian_executor: guardian_executor_addr,
account: account_addr,
guardian_provider,
})
.await
{
Ok(receipt) => {
let tx_hash = receipt.transaction_hash;
Ok(JsValue::from_str(&format!("0x{:x}", tx_hash)))
}
Err(e) => Err(JsValue::from_str(&format!(
"Failed to accept guardian: {}",
e
))),
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::wasm_transport::WasmHttpTransport;
use alloy::primitives::Address;
use alloy::providers::ProviderBuilder;
use alloy_rpc_client::RpcClient;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::guardian::list::get_guardians_list;

/// Get the list of guardians for a smart account
///
/// # Parameters
/// * `rpc_url` - RPC URL for the blockchain network
/// * `account_address` - Address of the smart account
/// * `guardian_executor_address` - Address of the GuardianExecutor contract
///
/// # Returns
/// Promise that resolves to a JSON array of guardian addresses (hex strings)
#[wasm_bindgen]
pub fn get_guardians_list_wasm(
rpc_url: String,
account_address: String,
guardian_executor_address: String,
) -> js_sys::Promise {
future_to_promise(async move {
// Parse addresses
let account_addr = match account_address.parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid account address: {}",
e
)));
}
};

let guardian_executor_addr =
match guardian_executor_address.parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid guardian executor address: {}",
e
)));
}
};

// Create transport and provider
let transport = WasmHttpTransport::new(rpc_url);
let client = RpcClient::new(transport.clone(), false);
let provider = ProviderBuilder::new().connect_client(client);

// Call the core function
match get_guardians_list(account_addr, guardian_executor_addr, provider)
.await
{
Ok(guardians) => {
let addresses: Vec<String> = guardians
.iter()
.map(|addr| format!("0x{:x}", addr))
.collect();
let json = serde_json::to_string(&addresses).map_err(|e| {
JsValue::from_str(&format!(
"Failed to serialize guardians list: {}",
e
))
})?;
Ok(JsValue::from_str(&json))
}
Err(e) => Err(JsValue::from_str(&format!(
"Failed to get guardians list: {}",
e
))),
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use crate::wasm_transport::WasmHttpTransport;
use alloy::{
network::EthereumWallet, primitives::Address, providers::ProviderBuilder,
signers::local::PrivateKeySigner,
};
use alloy_rpc_client::RpcClient;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use zksync_sso_erc4337_core::erc4337::{
account::modular_smart_account::guardian::propose::{
ProposeGuardianParams, propose_guardian,
},
bundler::{config::BundlerConfig, pimlico::client::BundlerClient},
signer::create_eoa_signer,
};

/// Propose a new guardian for a smart account
///
/// This function creates a user operation to propose a new guardian.
/// The account owner must sign the transaction.
///
/// # Parameters
/// * `config` - SendTransactionConfig with RPC URL, bundler URL, and entry point
/// * `guardian_executor` - Address of the GuardianExecutor contract
/// * `new_guardian` - Address of the guardian to propose
/// * `account` - Address of the smart account
/// * `eoa_validator_address` - Address of the EOA validator module
/// * `eoa_private_key` - Private key of the account owner (0x-prefixed hex string)
///
/// # Returns
/// Promise that resolves to the user operation receipt hash as a hex string
#[wasm_bindgen]
pub fn propose_guardian_wasm(
config: crate::SendTransactionConfig,
guardian_executor: String,
new_guardian: String,
account: String,
eoa_validator_address: String,
eoa_private_key: String,
) -> js_sys::Promise {
future_to_promise(async move {
// Parse addresses
let guardian_executor_addr = match guardian_executor.parse::<Address>()
{
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid guardian executor address: {}",
e
)));
}
};

let new_guardian_addr = match new_guardian.parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid new guardian address: {}",
e
)));
}
};

let account_addr = match account.parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid account address: {}",
e
)));
}
};

let entry_point_addr =
match config.entry_point_address().parse::<Address>() {
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid entry point address: {}",
e
)));
}
};

let eoa_validator_addr = match eoa_validator_address.parse::<Address>()
{
Ok(addr) => addr,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid EOA validator address: {}",
e
)));
}
};

// Parse EOA private key
let eoa_key = match eoa_private_key
.trim_start_matches("0x")
.parse::<PrivateKeySigner>()
{
Ok(signer) => signer,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Invalid EOA private key: {}",
e
)));
}
};

let eoa_wallet = EthereumWallet::from(eoa_key.clone());

// Create transport and provider
let transport = WasmHttpTransport::new(config.rpc_url());
let client = RpcClient::new(transport.clone(), false);
let provider =
ProviderBuilder::new().wallet(eoa_wallet).connect_client(client);

// Create bundler client
let bundler_client = {
let bundler_config = BundlerConfig::new(config.bundler_url());
BundlerClient::new(bundler_config)
};

// Create EOA signer
let eoa_key_str = format!("0x{}", hex::encode(eoa_key.to_bytes()));
let signer = match create_eoa_signer(eoa_key_str, eoa_validator_addr) {
Ok(signer) => signer,
Err(e) => {
return Err(JsValue::from_str(&format!(
"Failed to create EOA signer: {}",
e
)));
}
};

// Call the core function
match propose_guardian(ProposeGuardianParams {
guardian_executor: guardian_executor_addr,
new_guardian: new_guardian_addr,
account: account_addr,
entry_point: entry_point_addr,
paymaster: None,
provider,
bundler_client,
signer,
})
.await
{
Ok(receipt) => {
let tx_hash = receipt.receipt.transaction_hash;
Ok(JsValue::from_str(&tx_hash))
}
Err(e) => Err(JsValue::from_str(&format!(
"Failed to propose guardian: {}",
e
))),
}
})
}
Loading
Loading