diff --git a/packages/erc4337-contracts b/packages/erc4337-contracts index 8f2cef58f..403bc202f 160000 --- a/packages/erc4337-contracts +++ b/packages/erc4337-contracts @@ -1 +1 @@ -Subproject commit 8f2cef58fb923d0d359ed3ec1d6c7916f8cd2f5b +Subproject commit 403bc202f768f56faec540ee661f069f96eaa76c diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-cli/src/lib.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-cli/src/lib.rs index 9df3e634b..c96f126cf 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-cli/src/lib.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-cli/src/lib.rs @@ -40,6 +40,9 @@ enum Commands { #[arg(long)] session_validator_address: String, + + #[arg(long)] + guardian_executor_address: String, }, } @@ -53,6 +56,7 @@ async fn handle_command(command: Commands) -> eyre::Result<()> { webauthn_validator_address, eoa_validator_address, session_validator_address, + guardian_executor_address, } => { let config = CoreConfig::new( rpc_url.parse()?, @@ -68,6 +72,7 @@ async fn handle_command(command: Commands) -> eyre::Result<()> { webauthn_validator_address, eoa_validator_address, session_validator_address.to_string(), + guardian_executor_address.to_string(), )?, ); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/config/contracts.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/config/contracts.rs index ce43057ab..fff4ff441 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/config/contracts.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/config/contracts.rs @@ -9,6 +9,7 @@ pub struct Contracts { pub webauthn_validator: Address, pub eoa_validator: Address, pub session_validator: Address, + pub guardian_executor: Address, } impl Contracts { @@ -18,6 +19,7 @@ impl Contracts { webauthn_validator: Address, eoa_validator: Address, session_validator: Address, + guardian_executor: Address, ) -> Self { Self { entry_point, @@ -25,6 +27,7 @@ impl Contracts { webauthn_validator, eoa_validator, session_validator, + guardian_executor, } } @@ -34,6 +37,7 @@ impl Contracts { webauthn_validator: String, eoa_validator: String, session_validator: String, + guardian_executor: String, ) -> Result { Ok(Self::new( entry_point.parse::
().map_err(|e| { @@ -51,6 +55,9 @@ impl Contracts { session_validator.parse::
().map_err(|e| { ZkSyncSsoError::InvalidConfiguration(e.to_string()) })?, + guardian_executor.parse::
().map_err(|e| { + ZkSyncSsoError::InvalidConfiguration(e.to_string()) + })?, )) } } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337.rs index a9e668e27..69a160e63 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337.rs @@ -1,10 +1,8 @@ pub mod account; pub mod bundler; -pub mod client; pub mod entry_point; pub mod paymaster; pub mod send_call; -pub mod signature; pub mod signer; pub mod transaction; pub mod user_operation; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579.rs index 1f6f718e4..c81092591 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579.rs @@ -1,25 +1,4 @@ -pub mod add_module; pub mod calls; -pub mod module_installed; +pub mod module; -use alloy::sol; - -sol!( - #[sol(rpc)] - #[derive(Debug, Default)] - #[allow(missing_docs)] - IERC7579Account, - "../../../../../../packages/erc4337-contracts/out/IERC7579Account.sol/IERC7579Account.json" -); - -sol! { - #[sol(rpc)] - #[derive(Debug, Default)] - struct Execution { - address target; - uint256 value; - bytes data; - } - - function execute(bytes32 mode, bytes memory execution); -} +mod contract; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/add_module.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/add_module.rs deleted file mode 100644 index adeb5573d..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/add_module.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::erc4337::{ - account::{ - erc7579::{IERC7579Account, module_installed::is_module_installed}, - modular_smart_account::send::{SendParams, send_transaction}, - }, - bundler::pimlico::client::BundlerClient, - signer::Signer, -}; -use alloy::{ - primitives::{Address, Bytes, U256}, - providers::Provider, - sol_types::SolCall, -}; - -pub async fn add_module( - account_address: Address, - module_address: Address, - entry_point_address: Address, - provider: P, - bundler_client: BundlerClient, - signer: Signer, -) -> eyre::Result<()> { - let module_type_id = 1; - - let init_data = Bytes::default(); - - let call_data = - add_module_call_data(module_address, module_type_id, init_data); - - send_transaction(SendParams { - account: account_address, - entry_point: entry_point_address, - factory_payload: None, - call_data, - nonce_key: None, - paymaster: None, - bundler_client, - provider: provider.clone(), - signer, - }) - .await?; - - let is_expected_module_installed = - is_module_installed(module_address, account_address, provider.clone()) - .await?; - - eyre::ensure!( - is_expected_module_installed, - "{} is not installed", - module_address - ); - - Ok(()) -} - -fn add_module_call_data( - module: Address, - module_type_id: u8, - init_data: Bytes, -) -> Bytes { - IERC7579Account::installModuleCall { - moduleTypeId: U256::from(module_type_id), - module, - initData: init_data, - } - .abi_encode() - .into() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - erc4337::{ - account::modular_smart_account::{ - deploy::{DeployAccountParams, EOASigners, deploy_account}, - test_utilities::fund_account_with_default_amount, - }, - signer::create_eoa_signer, - }, - utils::alloy_utilities::test_utilities::{ - TestInfraConfig, - start_anvil_and_deploy_contracts_and_start_bundler_with_config, - }, - }; - use alloy::primitives::address; - - #[tokio::test] - async fn test_add_module() -> eyre::Result<()> { - let ( - _, - anvil_instance, - provider, - contracts, - signer_private_key, - bundler, - bundler_client, - ) = { - let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); - let config = TestInfraConfig { - signer_private_key: signer_private_key.clone(), - }; - start_anvil_and_deploy_contracts_and_start_bundler_with_config( - &config, - ) - .await? - }; - - let entry_point_address = - address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); - - let factory_address = contracts.account_factory; - let eoa_validator_address = contracts.eoa_validator; - - let signers = - vec![address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720")]; - - let eoa_signers = EOASigners { - addresses: signers, - validator_address: eoa_validator_address, - }; - - let account_address = deploy_account(DeployAccountParams { - factory_address, - eoa_signers: Some(eoa_signers), - webauthn_signer: None, - id: None, - provider: provider.clone(), - session_validator: None, - }) - .await?; - - println!("Account deployed"); - - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - account_address, - provider.clone(), - ) - .await?; - - eyre::ensure!( - is_eoa_module_installed, - "is_eoa_module_installed is not installed" - ); - - fund_account_with_default_amount(account_address, provider.clone()) - .await?; - - let module_address = contracts.webauthn_validator; - - let signer = create_eoa_signer( - signer_private_key.clone(), - eoa_validator_address, - )?; - - add_module( - account_address, - module_address, - entry_point_address, - provider.clone(), - bundler_client.clone(), - signer, - ) - .await?; - - let is_web_authn_module_installed = is_module_installed( - module_address, - account_address, - provider.clone(), - ) - .await?; - - eyre::ensure!( - is_web_authn_module_installed, - "is_web_authn_module is not installed" - ); - - drop(anvil_instance); - drop(bundler); - - Ok(()) - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/calls.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/calls.rs index 59272e111..0956ce219 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/calls.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/calls.rs @@ -1,9 +1,62 @@ -use crate::erc4337::account::erc7579::{Execution, executeCall}; +use crate::erc4337::account::erc7579::contract::{ + account::IERC7579Account::executeCall, + execution::Execution as ContractExecution, +}; use alloy::{ - primitives::{Bytes, FixedBytes}, + primitives::{Address, Bytes, FixedBytes, U256}, sol, sol_types::{SolCall, SolType}, }; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Execution { + pub target: Address, + pub value: U256, + pub call_data: Bytes, +} + +impl From for ContractExecution { + fn from(execution: Execution) -> Self { + ContractExecution { + target: execution.target, + value: execution.value, + callData: execution.call_data, + } + } +} + +pub fn encode_execution_call_data( + mode: FixedBytes<32>, + execution_call_data: Bytes, +) -> Bytes { + executeCall { mode, executionCalldata: execution_call_data } + .abi_encode() + .into() +} + +pub fn encoded_call_with_target_and_data( + target: Address, + data: Bytes, +) -> Bytes { + encoded_call_data(target, Some(data), None) +} + +pub fn encoded_call_data( + target: Address, + data: Option, + value: Option, +) -> Bytes { + let call = { + let value = value.unwrap_or(U256::from(0)); + let call_data = data.unwrap_or_default(); + Execution { target, value, call_data } + }; + + let calls = vec![call]; + encode_calls(calls).into() +} pub fn encode_calls(calls: Vec) -> Vec { if calls.clone().len() == 1 { @@ -15,7 +68,7 @@ pub fn encode_calls(calls: Vec) -> Vec { fn single_call(call: &Execution) -> Vec { let mode = mode_code_single(); - let execution: Bytes = [ + let execution_calldata: Bytes = [ call.target.as_slice(), call.value .as_le_bytes() @@ -24,11 +77,12 @@ fn single_call(call: &Execution) -> Vec { .copied() .collect::>() .as_slice(), - call.data.to_vec().as_slice(), + call.call_data.to_vec().as_slice(), ] .concat() .into(); - executeCall { mode, execution }.abi_encode() + + executeCall { mode, executionCalldata: execution_calldata }.abi_encode() } fn multi_call(calls: Vec) -> Vec { @@ -37,7 +91,7 @@ fn multi_call(calls: Vec) -> Vec { sol! { struct ExecutionParams { - Execution[] executions; + ContractExecution[] executions; } } @@ -46,15 +100,17 @@ struct ERC7579AccountExecute(executeCall); impl ERC7579AccountExecute { pub fn new(executions: Vec) -> Self { let mode = mode_code(); - let execution = { - let exectution_params = ExecutionParams { executions }; + let execution_calldata = { + let exectution_params = ExecutionParams { + executions: executions.into_iter().map(Into::into).collect(), + }; let execution_bytes = ::abi_encode_params( &exectution_params, ); Bytes::from(execution_bytes) }; - Self(executeCall { mode, execution }) + Self(executeCall { mode, executionCalldata: execution_calldata }) } pub fn encode(&self) -> Vec { @@ -89,7 +145,7 @@ mod tests { let calls = vec![Execution { target, value: U256::from(1), - data: Bytes::default(), + call_data: Bytes::default(), }]; let encoded = encode_calls(calls); @@ -114,8 +170,16 @@ mod tests { let target = address!("0xd5b7e333f346c92b6f6355ac62cc3f0ffa882bc3"); let calls = vec![ - Execution { target, value: U256::from(1), data: Bytes::default() }, - Execution { target, value: U256::from(2), data: Bytes::default() }, + Execution { + target, + value: U256::from(1), + call_data: Bytes::default(), + }, + Execution { + target, + value: U256::from(2), + call_data: Bytes::default(), + }, ]; let encoded = encode_calls(calls); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/contract.rs new file mode 100644 index 000000000..fcf24f397 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/contract.rs @@ -0,0 +1,27 @@ +use alloy::sol; + +pub mod account { + use super::*; + + sol!( + #[sol(rpc)] + #[derive(Debug, Default)] + #[allow(missing_docs)] + IERC7579Account, + "../../../../../../packages/erc4337-contracts/out/IERC7579Account.sol/IERC7579Account.json" + ); +} + +pub mod execution { + use super::*; + + sol!( + #[sol(rpc)] + #[derive(Debug, Default)] + #[allow(missing_docs)] + IERC7579TypeExporter, + "../../../../../../packages/erc4337-contracts/out/IERC7579TypeExporter.sol/IERC7579TypeExporter.json" + ); + + pub use self::IERC7579TypeExporter::Execution; +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module.rs new file mode 100644 index 000000000..ca85e562d --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module.rs @@ -0,0 +1,84 @@ +pub mod add; +pub mod installed; + +use alloy::primitives::{Address, U256}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Module { + pub address: Address, + pub module_type: ModuleType, +} + +impl Module { + pub fn eoa_validator(address: Address) -> Self { + Self::validator(address) + } + + pub fn webauthn_validator(address: Address) -> Self { + Self::validator(address) + } + + pub fn session_key_validator(address: Address) -> Self { + Self::validator(address) + } + + pub fn guardian_executor(address: Address) -> Self { + Self::executor(address) + } + + pub fn validator(address: Address) -> Self { + Self { address, module_type: ModuleType::Validator } + } + + pub fn executor(address: Address) -> Self { + Self { address, module_type: ModuleType::Executor } + } + + pub fn fallback(address: Address) -> Self { + Self { address, module_type: ModuleType::Fallback } + } + + pub fn hook(address: Address) -> Self { + Self { address, module_type: ModuleType::Hook } + } + + pub fn prevalidation_hook_erc1271(address: Address) -> Self { + Self { address, module_type: ModuleType::PrevalidationHookErc1271 } + } + + pub fn prevalidation_hook_erc4337(address: Address) -> Self { + Self { address, module_type: ModuleType::PrevalidationHookErc4337 } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ModuleType { + Validator = 1, + Executor = 2, + Fallback = 3, + Hook = 4, + PrevalidationHookErc1271 = 8, + PrevalidationHookErc4337 = 9, +} + +impl From for U256 { + fn from(val: ModuleType) -> Self { + U256::from(val as u8) + } +} + +impl TryFrom for ModuleType { + type Error = eyre::Report; + fn try_from(value: U256) -> Result { + let byte_value = value.to::(); + match byte_value { + 1 => Ok(ModuleType::Validator), + 2 => Ok(ModuleType::Executor), + 3 => Ok(ModuleType::Fallback), + 4 => Ok(ModuleType::Hook), + 8 => Ok(ModuleType::PrevalidationHookErc1271), + 9 => Ok(ModuleType::PrevalidationHookErc4337), + _ => Err(eyre::eyre!("Invalid module type")), + } + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/add.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/add.rs new file mode 100644 index 000000000..9bdfb1557 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/add.rs @@ -0,0 +1,306 @@ +use crate::erc4337::{ + account::{ + erc7579::{ + contract::account::IERC7579Account, + module::{ + Module, + installed::{IsModuleInstalledParams, is_module_installed}, + }, + }, + modular_smart_account::send::{SendUserOpParams, send_user_op}, + }, + bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, + signer::Signer, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, + sol_types::SolCall, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct AddModulePayload { + pub module: Module, + pub init_data: Option, +} + +impl AddModulePayload { + pub fn webauthn(address: Address) -> Self { + Self { module: Module::webauthn_validator(address), init_data: None } + } + + pub fn session_key(address: Address) -> Self { + Self { module: Module::session_key_validator(address), init_data: None } + } + + pub fn guardian(address: Address) -> Self { + Self { module: Module::guardian_executor(address), init_data: None } + } +} + +#[derive(Clone)] +pub struct AddModuleParams { + pub account_address: Address, + pub module: AddModulePayload, + pub entry_point_address: Address, + pub paymaster: Option, + pub provider: P, + pub bundler_client: BundlerClient, + pub signer: Signer, +} + +pub async fn add_module

(params: AddModuleParams

) -> eyre::Result<()> +where + P: Provider + Send + Sync + Clone, +{ + let AddModuleParams { + account_address, + module, + entry_point_address, + paymaster, + provider, + bundler_client, + signer, + } = params; + + let payload = module.clone(); + let module = payload.module; + let module_address = module.address; + + let call_data = add_module_call_data(payload); + + send_user_op(SendUserOpParams { + account: account_address, + entry_point: entry_point_address, + factory_payload: None, + call_data, + nonce_key: None, + paymaster, + bundler_client, + provider: provider.clone(), + signer, + }) + .await?; + + let is_expected_module_installed = + is_module_installed(IsModuleInstalledParams { + module, + account: account_address, + provider: provider.clone(), + }) + .await?; + + eyre::ensure!( + is_expected_module_installed, + "{} is not installed", + module_address + ); + + Ok(()) +} + +fn add_module_call_data(payload: AddModulePayload) -> Bytes { + IERC7579Account::installModuleCall { + moduleTypeId: payload.module.module_type.into(), + module: payload.module.address, + initData: payload.init_data.unwrap_or_default(), + } + .abi_encode() + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + test_utilities::fund_account_with_default_amount, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }; + use alloy::primitives::address; + + #[tokio::test] + async fn test_add_module() -> eyre::Result<()> { + let ( + _, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signers = + vec![address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720")]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::validator(eoa_validator_address), + account: account_address, + provider: provider.clone(), + }) + .await?; + + eyre::ensure!( + is_eoa_module_installed, + "is_eoa_module_installed is not installed" + ); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let module_address = contracts.webauthn_validator; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(module_address), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer, + }) + .await?; + + let is_web_authn_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(module_address), + account: account_address, + provider: provider.clone(), + }) + .await?; + + eyre::ensure!( + is_web_authn_module_installed, + "is_web_authn_module is not installed" + ); + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } + + #[tokio::test] + async fn test_add_guardian_module() -> eyre::Result<()> { + let ( + _, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/installed.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/installed.rs new file mode 100644 index 000000000..a9c53f2cb --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module/installed.rs @@ -0,0 +1,31 @@ +use crate::erc4337::account::erc7579::{ + contract::account::IERC7579Account, module::Module, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, +}; + +#[derive(Clone)] +pub struct IsModuleInstalledParams { + pub module: Module, + pub account: Address, + pub provider: P, +} + +pub async fn is_module_installed

( + params: IsModuleInstalledParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let IsModuleInstalledParams { module, account, provider } = params; + let account = IERC7579Account::new(account, provider.clone()); + let module_type = module.module_type; + let module_address = module.address; + let is_installed = account + .isModuleInstalled(module_type.into(), module_address, Bytes::default()) + .call() + .await?; + Ok(is_installed) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module_installed.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module_installed.rs deleted file mode 100644 index c87b54cdb..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/erc7579/module_installed.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::erc4337::account::erc7579::IERC7579Account; -use alloy::{ - primitives::{Address, Bytes, U256}, - providers::Provider, -}; -use eyre::{self, Ok}; - -pub async fn is_module_installed( - module: Address, - account: Address, - provider: P, -) -> eyre::Result { - let account = IERC7579Account::new(account, provider.clone()); - let module_type_id = U256::from(1); - let is_installed = account - .isModuleInstalled(module_type_id, module, Bytes::default()) - .call() - .await?; - Ok(is_installed) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account.rs index fdd1ab399..d23758cf1 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account.rs @@ -1,36 +1,12 @@ -pub mod add_passkey; pub mod deploy; -pub mod nonce; +pub mod guardian; +pub mod passkey; pub mod send; pub mod session; pub mod signers; pub mod utils; -#[cfg(test)] -pub mod test_utilities; - -use alloy::sol; - -sol!( - #[derive(Debug, Default)] - #[allow(missing_docs)] - #[sol(rpc)] - MSAFactory, - "../../../../../../packages/erc4337-contracts/out/MSAFactory.sol/MSAFactory.json" -); +mod contract; -sol!( - #[derive(Debug, Default)] - #[allow(missing_docs)] - #[sol(rpc)] - ModularSmartAccount, - "../../../../../../packages/erc4337-contracts/out/ModularSmartAccount.sol/ModularSmartAccount.json" -); - -sol!( - #[derive(Debug, Default)] - #[allow(missing_docs)] - #[sol(rpc)] - WebAuthnValidator, - "../../../../../../packages/erc4337-contracts/out/WebAuthnValidator.sol/WebAuthnValidator.json" -); +#[cfg(any(feature = "test-utilities", test))] +pub mod test_utilities; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/contract.rs new file mode 100644 index 000000000..3e6128cd5 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/contract.rs @@ -0,0 +1,17 @@ +use alloy::sol; + +sol!( + #[derive(Debug, Default)] + #[allow(missing_docs)] + #[sol(rpc)] + MSAFactory, + "../../../../../../packages/erc4337-contracts/out/MSAFactory.sol/MSAFactory.json" +); + +sol!( + #[derive(Debug, Default)] + #[allow(missing_docs)] + #[sol(rpc)] + ModularSmartAccount, + "../../../../../../packages/erc4337-contracts/out/ModularSmartAccount.sol/ModularSmartAccount.json" +); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy.rs index 59a661828..85a35a7d1 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy.rs @@ -2,9 +2,11 @@ pub mod user_op; use crate::erc4337::{ account::modular_smart_account::{ - MSAFactory::{self, deployAccountCall}, - ModularSmartAccount::initializeAccountCall, - add_passkey::PasskeyPayload, + contract::{ + MSAFactory, MSAFactory::deployAccountCall, + ModularSmartAccount::initializeAccountCall, + }, + passkey::add::PasskeyPayload, }, utils::check_deployed::{Contract, check_contract_deployed}, }; @@ -116,6 +118,22 @@ where Ok(address) } +pub fn deploy_accout_call_data( + account_id: FixedBytes<32>, + init_data: Bytes, +) -> Bytes { + let call = + MSAFactory::deployAccountCall { salt: account_id, initData: init_data }; + call.abi_encode().into() +} + +pub fn initialize_account_call_data( + modules: Vec

, + data: Vec, +) -> Bytes { + MSAInitializeAccount::new(modules, data).encode().into() +} + fn get_account_created_address( receipt: &TransactionReceipt, ) -> eyre::Result
{ @@ -137,7 +155,7 @@ fn create_init_data( ) -> Bytes { let (data, modules) = modules_from_signers(eoa_signers, webauthn_signer, session_validator); - MSAInitializeAccount::new(modules, data).encode().into() + initialize_account_call_data(modules, data) } fn modules_from_signers( @@ -194,10 +212,44 @@ fn generate_random_id() -> [u8; 32] { mod tests { use super::*; use crate::{ - erc4337::account::erc7579::module_installed::is_module_installed, - utils::alloy_utilities::test_utilities::start_anvil_and_deploy_contracts, + erc4337::{ + account::{ + erc7579::{ + calls::{Execution, encode_calls}, + module::{ + Module, + installed::{ + IsModuleInstalledParams, is_module_installed, + }, + }, + }, + modular_smart_account::{ + send::{SendUserOpParams, send_user_op}, + session::{ + create::{CreateSessionParams, create_session}, + send::keyed_nonce, + session_lib::session_spec::{ + SessionSpec, limit_type::LimitType, + transfer_spec::TransferSpec, + usage_limit::UsageLimit, + }, + signature::session_signature, + }, + test_utilities::fund_account_with_default_amount, + }, + }, + signer::{Signer, create_eoa_signer}, + }, + utils::alloy_utilities::test_utilities::{ + TestInfraConfig, start_anvil_and_deploy_contracts, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, }; - use alloy::primitives::{U256, Uint, address}; + use alloy::{ + primitives::{U256, Uint, address}, + signers::local::PrivateKeySigner, + }; + use std::{future::Future, pin::Pin, str::FromStr, sync::Arc}; #[tokio::test] async fn test_deploy_account_basic() -> eyre::Result<()> { @@ -249,12 +301,13 @@ mod tests { println!("Account deployed"); - let is_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_module_installed, "Module is not installed"); drop(anvil_instance); @@ -286,12 +339,15 @@ mod tests { println!("Account deployed with session validator"); - let is_module_installed = is_module_installed( - session_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator( + session_validator_address, + ), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_module_installed, "Session validator module is not installed" @@ -336,11 +392,11 @@ mod tests { println!("Account deployed with EOA and session validators"); // Verify EOA validator is installed - let is_eoa_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) + let is_eoa_installed = is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) .await?; eyre::ensure!( is_eoa_installed, @@ -348,12 +404,15 @@ mod tests { ); // Verify session validator is installed - let is_session_installed = is_module_installed( - session_validator_address, - address, - provider.clone(), - ) - .await?; + let is_session_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator( + session_validator_address, + ), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_installed, "Session validator module is not installed" @@ -367,35 +426,6 @@ mod tests { /// Test comprehensive session flow: deploy with session validator, create session, and transact #[tokio::test] async fn test_deploy_with_session_and_transact() -> eyre::Result<()> { - use crate::{ - erc4337::{ - account::{ - erc7579::{Execution, calls::encode_calls}, - modular_smart_account::{ - send::{SendParams, send_transaction}, - session::{ - create::create_session, - send::keyed_nonce, - session_lib::session_spec::{ - SessionSpec, limit_type::LimitType, - transfer_spec::TransferSpec, - usage_limit::UsageLimit, - }, - signature::session_signature, - }, - test_utilities::fund_account_with_default_amount, - }, - }, - signer::{Signer, create_eoa_signer}, - }, - utils::alloy_utilities::test_utilities::{ - TestInfraConfig, - start_anvil_and_deploy_contracts_and_start_bundler_with_config, - }, - }; - use alloy::signers::local::PrivateKeySigner; - use std::{future::Future, pin::Pin, str::FromStr, sync::Arc}; - // Start test infrastructure let ( _, @@ -452,20 +482,23 @@ mod tests { println!("✓ Account deployed with session validator pre-installed"); // Verify both modules are installed - let is_eoa_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) + let is_eoa_installed = is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) .await?; eyre::ensure!(is_eoa_installed, "EOA validator not installed"); - let is_session_installed = is_module_installed( - session_validator_address, - address, - provider.clone(), - ) - .await?; + let is_session_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator( + session_validator_address, + ), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_session_installed, "Session validator not installed"); println!("✓ Both EOA and Session validators verified as installed"); @@ -503,15 +536,16 @@ mod tests { }], }; - create_session( - address, - session_spec.clone(), + create_session(CreateSessionParams { + account_address: address, + spec: session_spec.clone(), entry_point_address, - session_validator_address, - bundler_client.clone(), - provider.clone(), - eoa_signer, - ) + session_key_validator: session_validator_address, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: eoa_signer, + }) .await?; println!("✓ Session created successfully"); @@ -519,8 +553,8 @@ mod tests { // Send transaction using the session key let call = { let value = U256::from(1_000_000_000_000_000u64); // 0.001 ETH - let data = Bytes::default(); - Execution { target, value, data } + let call_data = Bytes::default(); + Execution { target, value, call_data } }; let calls = vec![call]; @@ -568,7 +602,7 @@ mod tests { let keyed_nonce = keyed_nonce(session_signer_address); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: address, entry_point: entry_point_address, factory_payload: None, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy/user_op.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy/user_op.rs index 68a7bc9dc..8ad65aa12 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy/user_op.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/deploy/user_op.rs @@ -4,7 +4,7 @@ use crate::erc4337::{ DeployAccountParams, MSADeployAccount, create_init_data, generate_random_account_id, get_account_created_address, }, - send::{FactoryPayload, SendParams, send_transaction}, + send::{FactoryPayload, SendUserOpParams, send_user_op}, }, bundler::pimlico::client::BundlerClient, entry_point::sender_address::get_sender_address, @@ -69,7 +69,7 @@ where ) .await?; - let user_op_receipt = send_transaction(SendParams { + let user_op_receipt = send_user_op(SendUserOpParams { account: predicted_account_address, entry_point: entry_point_address, factory_payload: Some(FactoryPayload { @@ -117,7 +117,10 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::module_installed::is_module_installed, + erc7579::module::{ + Module, + installed::{IsModuleInstalledParams, is_module_installed}, + }, modular_smart_account::{ deploy::EOASigners, test_utilities::fund_account_with_default_amount, @@ -201,12 +204,13 @@ mod tests { fund_account_with_default_amount(address, provider.clone()).await?; - let is_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_module_installed, "Module is not installed"); drop(mock_paymaster); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian.rs new file mode 100644 index 000000000..39b48a9b1 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian.rs @@ -0,0 +1,8 @@ +pub mod accept; +pub mod list; +pub mod propose; +pub mod recovery; +pub mod remove; +pub mod status; + +mod contract; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/accept.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/accept.rs new file mode 100644 index 000000000..4ee8157e7 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/accept.rs @@ -0,0 +1,221 @@ +use crate::erc4337::account::modular_smart_account::guardian::contract::GuardianExecutor; +use alloy::{ + primitives::Address, providers::Provider, rpc::types::TransactionReceipt, +}; + +#[derive(Clone)] +pub struct AcceptGuardianParams { + pub guardian_executor: Address, + pub account: Address, + pub guardian_provider: P, +} + +pub async fn accept_guardian

( + params: AcceptGuardianParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let AcceptGuardianParams { guardian_executor, account, guardian_provider } = + params; + + let guardian_executor_instance = + GuardianExecutor::new(guardian_executor, guardian_provider); + + let receipt = guardian_executor_instance + .acceptGuardian(account) + .send() + .await? + .get_receipt() + .await?; + + Ok(receipt) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::{ + erc7579::module::add::{ + AddModuleParams, AddModulePayload, add_module, + }, + modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + guardian::{ + list::get_guardians_list, + propose::{ProposeGuardianParams, propose_guardian}, + status::get_guardian_status, + }, + test_utilities::fund_account_with_default_amount, + }, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::{ + ethereum_wallet_from_private_key, + test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }, + }; + use alloy::{primitives::address, providers::ProviderBuilder}; + + #[tokio::test] + async fn test_accept_guardian() -> eyre::Result<()> { + let ( + node_url, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + // Install WebAuthn module + { + let webauthn_module = contracts.webauthn_validator; + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(webauthn_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + } + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Anvil Rich Wallet #8 as guardian + let guardian_address = + address!("0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f"); + let guardian_private_key = "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97".to_string(); + + // Propose guardian + propose_guardian(ProposeGuardianParams { + guardian_executor: guardian_module, + new_guardian: guardian_address, + account: account_address, + entry_point: entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + println!("\n\n\n\n\n\n\nGuardian proposed\n\n\n\n\n\n"); + + // Accept guardian + { + let guardian_wallet = + ethereum_wallet_from_private_key(&guardian_private_key)?; + let guardian_provider = ProviderBuilder::new() + .wallet(guardian_wallet) + .connect_http(node_url); + + accept_guardian(AcceptGuardianParams { + guardian_executor: guardian_module, + account: account_address, + guardian_provider, + }) + .await?; + } + + println!("\n\n\n\n\n\n\nGuardian accepted\n\n\n\n\n\n"); + + // Verify guardian was accepted correctly + { + let guardians = get_guardians_list( + account_address, + guardian_module, + provider.clone(), + ) + .await?; + eyre::ensure!( + guardians.len() == 1, + "Expected exactly one guardian in the list" + ); + eyre::ensure!( + guardians[0] == guardian_address, + "Guardian address mismatch" + ); + + let status = get_guardian_status( + account_address, + guardian_address, + guardian_module, + provider.clone(), + ) + .await?; + eyre::ensure!(status.is_active(), "Guardian should be active"); + } + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/contract.rs new file mode 100644 index 000000000..0ce9265d2 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/contract.rs @@ -0,0 +1,9 @@ +use alloy::sol; + +sol!( + #[sol(rpc)] + #[derive(Debug, Default)] + #[allow(missing_docs)] + GuardianExecutor, + "../../../../../../packages/erc4337-contracts/out/GuardianExecutor.sol/GuardianExecutor.json" +); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/list.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/list.rs new file mode 100644 index 000000000..3f891a368 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/list.rs @@ -0,0 +1,17 @@ +use crate::erc4337::account::modular_smart_account::guardian::contract::GuardianExecutor; +use alloy::{primitives::Address, providers::Provider}; + +pub async fn get_guardians_list

( + account_address: Address, + guardian_executor_address: Address, + provider: P, +) -> eyre::Result> +where + P: Provider + Send + Sync + Clone, +{ + let guardian_executor = + GuardianExecutor::new(guardian_executor_address, provider); + let guardians = + guardian_executor.guardiansFor(account_address).call().await?; + Ok(guardians) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/propose.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/propose.rs new file mode 100644 index 000000000..38809e6fc --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/propose.rs @@ -0,0 +1,248 @@ +use crate::erc4337::{ + account::{ + erc7579::calls::encoded_call_with_target_and_data, + modular_smart_account::{ + guardian::contract::GuardianExecutor, + send::{SendUserOpParams, send_user_op}, + }, + }, + bundler::{ + models::receipt::UserOperationReceipt, pimlico::client::BundlerClient, + }, + paymaster::params::PaymasterParams, + signer::Signer, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, + sol_types::SolCall, +}; + +#[derive(Clone)] +pub struct ProposeGuardianParams { + pub guardian_executor: Address, + pub new_guardian: Address, + pub account: Address, + pub entry_point: Address, + pub paymaster: Option, + pub provider: P, + pub bundler_client: BundlerClient, + pub signer: Signer, +} + +pub async fn propose_guardian

( + params: ProposeGuardianParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let ProposeGuardianParams { + guardian_executor, + new_guardian, + account, + entry_point, + paymaster, + provider, + bundler_client, + signer, + } = params; + + let call_data = propose_guardian_call_data(new_guardian, guardian_executor); + + let user_op_receipt = send_user_op(SendUserOpParams { + account, + entry_point, + factory_payload: None, + call_data, + nonce_key: None, + paymaster, + bundler_client, + provider, + signer, + }) + .await?; + + Ok(user_op_receipt) +} + +pub fn propose_guardian_call_data( + new_guardian: Address, + guardian_executor: Address, +) -> Bytes { + let propose_guardian_calldata = + GuardianExecutor::proposeGuardianCall { newGuardian: new_guardian } + .abi_encode() + .into(); + + encoded_call_with_target_and_data( + guardian_executor, + propose_guardian_calldata, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::{ + erc7579::module::add::{ + AddModuleParams, AddModulePayload, add_module, + }, + modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + guardian::{ + list::get_guardians_list, status::get_guardian_status, + }, + test_utilities::fund_account_with_default_amount, + }, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }; + use alloy::primitives::address; + + #[tokio::test] + async fn test_propose_guardian() -> eyre::Result<()> { + let ( + _, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + // Install WebAuthn module + { + let webauthn_module = contracts.webauthn_validator; + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(webauthn_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + } + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Use a different address for the guardian (not the account owner) + let new_guardian = + address!("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"); + + // Propose guardian + propose_guardian(ProposeGuardianParams { + guardian_executor: guardian_module, + new_guardian, + account: account_address, + entry_point: entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Verify guardian was proposed correctly + { + // Check guardians list + let guardians = get_guardians_list( + account_address, + guardian_module, + provider.clone(), + ) + .await?; + eyre::ensure!( + guardians.len() == 1, + "Expected exactly one guardian in the list" + ); + eyre::ensure!( + guardians[0] == new_guardian, + "Guardian address mismatch" + ); + + // Check guardian status (should be present but not active) + let status = get_guardian_status( + account_address, + new_guardian, + guardian_module, + provider.clone(), + ) + .await?; + eyre::ensure!( + status.is_present_but_not_active(), + "Guardian should be present but not active" + ); + } + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery.rs new file mode 100644 index 000000000..c470ca5eb --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery.rs @@ -0,0 +1,33 @@ +pub mod finalize; +pub mod initialize; +pub mod status; + +use crate::erc4337::account::modular_smart_account::guardian::contract::GuardianExecutor::RecoveryType as ContractRecoveryType; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum RecoveryType { + None, + EOA, + Passkey, +} + +impl From for ContractRecoveryType { + fn from(recovery_type: RecoveryType) -> Self { + match recovery_type { + RecoveryType::None => 0.into(), + RecoveryType::EOA => 1.into(), + RecoveryType::Passkey => 2.into(), + } + } +} + +impl From for u8 { + fn from(recovery_type: RecoveryType) -> Self { + match recovery_type { + RecoveryType::None => 0, + RecoveryType::EOA => 1, + RecoveryType::Passkey => 2, + } + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/finalize.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/finalize.rs new file mode 100644 index 000000000..5ed51727a --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/finalize.rs @@ -0,0 +1,267 @@ +use crate::erc4337::account::modular_smart_account::guardian::contract::GuardianExecutor; +use alloy::{ + primitives::Address, providers::Provider, rpc::types::TransactionReceipt, +}; + +#[derive(Clone)] +pub struct FinalizeRecoveryParams { + pub guardian_executor: Address, + pub account: Address, + pub guardian_provider: P, +} + +pub async fn finalize_recovery

( + params: FinalizeRecoveryParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let FinalizeRecoveryParams { + guardian_executor, + account, + guardian_provider, + } = params; + + let guardian_executor_instance = + GuardianExecutor::new(guardian_executor, guardian_provider); + + let receipt = guardian_executor_instance + .finalizeRecovery(account) + .send() + .await? + .get_receipt() + .await?; + + Ok(receipt) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::{ + erc7579::module::add::{ + AddModuleParams, AddModulePayload, add_module, + }, + modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + guardian::{ + accept::{AcceptGuardianParams, accept_guardian}, + propose::{ProposeGuardianParams, propose_guardian}, + recovery::{ + RecoveryType, + initialize::{ + InitializeRecoveryParams, initialize_recovery, + }, + status::get_recovery_status, + }, + }, + signers::eoa::active::get_active_owners, + test_utilities::fund_account_with_default_amount, + utils::advance_time, + }, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::{ + ethereum_wallet_from_private_key, + test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }, + }; + use alloy::{ + primitives::address, providers::ProviderBuilder, sol_types::SolValue, + }; + + #[tokio::test] + async fn test_finalize_recovery() -> eyre::Result<()> { + let ( + node_url, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + // Install WebAuthn module + { + let webauthn_module = contracts.webauthn_validator; + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(webauthn_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + } + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Anvil Rich Wallet #8 as guardian + let guardian_address = + address!("0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f"); + let guardian_private_key = "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97".to_string(); + let guardian_wallet = + ethereum_wallet_from_private_key(&guardian_private_key)?; + let guardian_provider = ProviderBuilder::new() + .wallet(guardian_wallet) + .connect_http(node_url.clone()); + + // Propose guardian + propose_guardian(ProposeGuardianParams { + guardian_executor: guardian_module, + new_guardian: guardian_address, + account: account_address, + entry_point: entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Accept guardian + accept_guardian(AcceptGuardianParams { + guardian_executor: guardian_module, + account: account_address, + guardian_provider: guardian_provider.clone(), + }) + .await?; + + // New owner: Alloy Rich Wallet #7 + let new_owner_address = + address!("0x14dC79964da2C08b23698B3D3cc7Ca32193d9955"); + + // Initialize recovery directly + { + let initialize_recovery_data = + new_owner_address.abi_encode().into(); + initialize_recovery(InitializeRecoveryParams { + guardian_executor: guardian_module, + account: account_address, + recovery_type: RecoveryType::EOA, + data: initialize_recovery_data, + guardian_provider: guardian_provider.clone(), + }) + .await?; + } + + println!("\n\n\n\n\n\n\n\nRecovery initialized\n\n\n\n\n\n\n\n"); + + // Advance time by 2 days + // This is required because finalizeRecovery requires REQUEST_DELAY_TIME (24 hours) to pass + { + let two_days_in_seconds = 2 * 24 * 60 * 60; // 2 days in seconds + advance_time(&provider, two_days_in_seconds).await?; + } + + // Finalize recovery + finalize_recovery(FinalizeRecoveryParams { + guardian_executor: guardian_module, + account: account_address, + guardian_provider: guardian_provider.clone(), + }) + .await?; + + // Verify recovery was finalized correctly + { + let recovery_status = get_recovery_status( + account_address, + guardian_address, + guardian_module, + provider.clone(), + ) + .await? + .ok_or(eyre::eyre!("Recovery status not found"))?; + + eyre::ensure!( + recovery_status.is_finalized(), + "Recovery should be finalized" + ); + + // Verify new owner was added + let active_owners = get_active_owners( + account_address, + provider.clone(), + contracts.clone(), + ) + .await?; + + eyre::ensure!( + active_owners.contains(&new_owner_address), + "New owner should be added to the account" + ); + } + + println!("\n\n\n\n\n\n\n\nRecovery finalized\n\n\n\n\n\n\n\n"); + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/initialize.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/initialize.rs new file mode 100644 index 000000000..e946af85c --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/initialize.rs @@ -0,0 +1,235 @@ +use crate::erc4337::account::modular_smart_account::guardian::{ + contract::GuardianExecutor, recovery::RecoveryType, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, + rpc::types::TransactionReceipt, +}; + +#[derive(Clone)] +pub struct InitializeRecoveryParams { + pub guardian_executor: Address, + pub account: Address, + pub recovery_type: RecoveryType, + pub data: Bytes, + pub guardian_provider: P, +} + +pub async fn initialize_recovery

( + params: InitializeRecoveryParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let InitializeRecoveryParams { + guardian_executor, + account, + recovery_type, + data, + guardian_provider, + } = params; + + let guardian_executor_instance = + GuardianExecutor::new(guardian_executor, guardian_provider); + + let receipt = guardian_executor_instance + .initializeRecovery(account, recovery_type.into(), data) + .send() + .await? + .get_receipt() + .await?; + + Ok(receipt) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::{ + erc7579::module::add::{ + AddModuleParams, AddModulePayload, add_module, + }, + modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + guardian::{ + accept::{AcceptGuardianParams, accept_guardian}, + propose::{ProposeGuardianParams, propose_guardian}, + recovery::status::get_recovery_status, + }, + test_utilities::fund_account_with_default_amount, + }, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::{ + ethereum_wallet_from_private_key, + test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }, + }; + use alloy::{ + primitives::address, providers::ProviderBuilder, sol_types::SolValue, + }; + + #[tokio::test] + async fn test_initialize_recovery() -> eyre::Result<()> { + let ( + node_url, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + // Install WebAuthn module + { + let webauthn_module = contracts.webauthn_validator; + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(webauthn_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + } + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Anvil Rich Wallet #8 as guardian + let guardian_address = + address!("0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f"); + let guardian_private_key = "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97".to_string(); + let guardian_wallet = + ethereum_wallet_from_private_key(&guardian_private_key)?; + let guardian_provider = ProviderBuilder::new() + .wallet(guardian_wallet) + .connect_http(node_url.clone()); + + // Propose guardian + propose_guardian(ProposeGuardianParams { + guardian_executor: guardian_module, + new_guardian: guardian_address, + account: account_address, + entry_point: entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Accept guardian + accept_guardian(AcceptGuardianParams { + guardian_executor: guardian_module, + account: account_address, + guardian_provider: guardian_provider.clone(), + }) + .await?; + + // New owner: Alloy Rich Wallet #7 + let new_owner_address = + address!("0x14dC79964da2C08b23698B3D3cc7Ca32193d9955"); + + // Initialize recovery directly + { + let initialize_recovery_data = + new_owner_address.abi_encode().into(); + initialize_recovery(InitializeRecoveryParams { + guardian_executor: guardian_module, + account: account_address, + recovery_type: RecoveryType::EOA, + data: initialize_recovery_data, + guardian_provider: guardian_provider.clone(), + }) + .await?; + } + + // Verify recovery was initialized correctly + { + let recovery_status = get_recovery_status( + account_address, + guardian_address, + guardian_module, + provider.clone(), + ) + .await? + .ok_or(eyre::eyre!("Recovery status not found"))?; + + eyre::ensure!( + recovery_status.is_initialized(), + "Recovery should be initialized" + ); + } + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/status.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/status.rs new file mode 100644 index 000000000..fac368c49 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/recovery/status.rs @@ -0,0 +1,138 @@ +use crate::erc4337::account::modular_smart_account::{ + guardian::contract::GuardianExecutor, + utils::{calculate_from_block, create_logs_filter_with_range}, +}; +use alloy::{ + primitives::Address, providers::Provider, rpc::types::Log, + sol_types::SolEvent, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum RecoveryStatus { + Initialized, + Finalized, +} + +impl RecoveryStatus { + pub fn is_initialized(&self) -> bool { + matches!(self, RecoveryStatus::Initialized) + } + + pub fn is_finalized(&self) -> bool { + matches!(self, RecoveryStatus::Finalized) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RecoveryEventType { + Initiated, + Finished, + Discarded, +} + +pub async fn get_recovery_status

( + account_address: Address, + guardian_address: Address, + guardian_executor_address: Address, + provider: P, +) -> eyre::Result> +where + P: Provider + Send + Sync + Clone, +{ + let from_block = { + let current_block = provider.get_block_number().await?; + calculate_from_block(current_block) + }; + + let filter = + create_logs_filter_with_range(guardian_executor_address, from_block); + + let all_logs = provider.get_logs(&filter).await?; + + let recovery_events = + parse_recovery_events(all_logs, account_address, guardian_address)?; + + let status = determine_recovery_status(recovery_events); + + Ok(status) +} + +fn parse_recovery_events( + logs: Vec, + account_address: Address, + guardian_address: Address, +) -> eyre::Result> { + let mut events = Vec::new(); + + for log in logs { + let Some(topic0) = log.inner.topics().first() else { + continue; + }; + + match *topic0 { + GuardianExecutor::RecoveryInitiated::SIGNATURE_HASH => { + if let Ok(decoded) = + log.log_decode::() + { + let event = decoded.inner.data; + if event.account == account_address + && event.guardian == guardian_address + { + events.push(RecoveryEventType::Initiated); + } + } + } + GuardianExecutor::RecoveryFinished::SIGNATURE_HASH => { + if let Ok(decoded) = + log.log_decode::() + { + let event = decoded.inner.data; + if event.account == account_address { + events.push(RecoveryEventType::Finished); + } + } + } + GuardianExecutor::RecoveryDiscarded::SIGNATURE_HASH => { + if let Ok(decoded) = + log.log_decode::() + { + let event = decoded.inner.data; + if event.account == account_address { + events.push(RecoveryEventType::Discarded); + } + } + } + _ => continue, + } + } + + Ok(events) +} + +fn determine_recovery_status( + events: Vec, +) -> Option { + // The last event in the list is the most recent (logs come in chronological order) + // Process from the end to find the most recent relevant event + // Since there can only be one pending recovery per account at a time, + // the most recent event determines the current status + + // Iterate backwards to find the most recent event + let event_type = events.iter().next_back()?; + match *event_type { + RecoveryEventType::Finished => { + // Recovery was finalized - this is the final state + Some(RecoveryStatus::Finalized) + } + RecoveryEventType::Discarded => { + // Recovery was discarded - no recovery in progress + None + } + RecoveryEventType::Initiated => { + // Recovery was initiated - check if there's a Finished/Discarded after it + // If we're here, we haven't seen a Finished/Discarded yet, so recovery is initialized + Some(RecoveryStatus::Initialized) + } + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/remove.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/remove.rs new file mode 100644 index 000000000..1f384f461 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/remove.rs @@ -0,0 +1,268 @@ +use crate::erc4337::{ + account::{ + erc7579::calls::encoded_call_with_target_and_data, + modular_smart_account::{ + guardian::contract::GuardianExecutor, + send::{SendUserOpParams, send_user_op}, + }, + }, + bundler::{ + models::receipt::UserOperationReceipt, pimlico::client::BundlerClient, + }, + paymaster::params::PaymasterParams, + signer::Signer, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, + sol_types::SolCall, +}; + +#[derive(Clone)] +pub struct RemoveGuardianParams { + guardian_executor: Address, + guardian_to_remove: Address, + account_address: Address, + entry_point_address: Address, + paymaster: Option, + provider: P, + bundler_client: BundlerClient, + signer: Signer, +} + +pub async fn remove_guardian

( + params: RemoveGuardianParams

, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let RemoveGuardianParams { + guardian_executor, + guardian_to_remove, + account_address, + entry_point_address, + paymaster, + provider, + bundler_client, + signer, + } = params; + + let call_data = + remove_guardian_call_data(guardian_to_remove, guardian_executor); + + let receipt = send_user_op(SendUserOpParams { + account: account_address, + entry_point: entry_point_address, + factory_payload: None, + call_data, + nonce_key: None, + paymaster, + bundler_client, + provider, + signer, + }) + .await?; + + Ok(receipt) +} + +pub fn remove_guardian_call_data( + guardian_to_remove: Address, + guardian_executor: Address, +) -> Bytes { + let propose_guardian_calldata = GuardianExecutor::removeGuardianCall { + guardianToRemove: guardian_to_remove, + } + .abi_encode() + .into(); + + encoded_call_with_target_and_data( + guardian_executor, + propose_guardian_calldata, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + erc4337::{ + account::{ + erc7579::module::add::{ + AddModuleParams, AddModulePayload, add_module, + }, + modular_smart_account::{ + deploy::{DeployAccountParams, EOASigners, deploy_account}, + guardian::{ + accept::{AcceptGuardianParams, accept_guardian}, + list::get_guardians_list, + propose::{ProposeGuardianParams, propose_guardian}, + }, + test_utilities::fund_account_with_default_amount, + }, + }, + signer::create_eoa_signer, + }, + utils::alloy_utilities::{ + ethereum_wallet_from_private_key, + test_utilities::{ + TestInfraConfig, + start_anvil_and_deploy_contracts_and_start_bundler_with_config, + }, + }, + }; + use alloy::{primitives::address, providers::ProviderBuilder}; + + #[tokio::test] + async fn test_remove_guardian() -> eyre::Result<()> { + let ( + node_url, + anvil_instance, + provider, + contracts, + signer_private_key, + bundler, + bundler_client, + ) = { + let signer_private_key = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6".to_string(); + let config = TestInfraConfig { + signer_private_key: signer_private_key.clone(), + }; + start_anvil_and_deploy_contracts_and_start_bundler_with_config( + &config, + ) + .await? + }; + + let entry_point_address = + address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); + + let factory_address = contracts.account_factory; + let eoa_validator_address = contracts.eoa_validator; + + let signer_address = + address!("0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"); + let signers = vec![signer_address]; + + let eoa_signers = EOASigners { + addresses: signers, + validator_address: eoa_validator_address, + }; + + let account_address = deploy_account(DeployAccountParams { + factory_address, + eoa_signers: Some(eoa_signers), + webauthn_signer: None, + session_validator: None, + id: None, + provider: provider.clone(), + }) + .await?; + + println!("Account deployed"); + + fund_account_with_default_amount(account_address, provider.clone()) + .await?; + + let signer = create_eoa_signer( + signer_private_key.clone(), + eoa_validator_address, + )?; + + // Install WebAuthn module + { + let webauthn_module = contracts.webauthn_validator; + add_module(AddModuleParams { + account_address, + module: AddModulePayload::webauthn(webauthn_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + } + + let guardian_module = contracts.guardian_executor; + + // Install Guardian module + add_module(AddModuleParams { + account_address, + module: AddModulePayload::guardian(guardian_module), + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Anvil Rich Wallet #8 as guardian + let guardian_address = + address!("0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f"); + let guardian_private_key = "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97".to_string(); + + // Propose guardian + propose_guardian(ProposeGuardianParams { + guardian_executor: guardian_module, + new_guardian: guardian_address, + account: account_address, + entry_point: entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Accept guardian + { + let guardian_wallet = + ethereum_wallet_from_private_key(&guardian_private_key)?; + let guardian_provider = ProviderBuilder::new() + .wallet(guardian_wallet) + .connect_http(node_url); + + accept_guardian(AcceptGuardianParams { + guardian_executor: guardian_module, + account: account_address, + guardian_provider, + }) + .await?; + } + + // Remove guardian + remove_guardian(RemoveGuardianParams { + guardian_executor: guardian_module, + guardian_to_remove: guardian_address, + account_address, + entry_point_address, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) + .await?; + + // Verify guardian was removed correctly + { + let guardians = get_guardians_list( + account_address, + guardian_module, + provider.clone(), + ) + .await?; + eyre::ensure!( + guardians.is_empty(), + "Expected no guardians in the list, got {:?}", + guardians + ); + } + + drop(anvil_instance); + drop(bundler); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/status.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/status.rs new file mode 100644 index 000000000..4c99ffe90 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/guardian/status.rs @@ -0,0 +1,54 @@ +use crate::erc4337::account::modular_smart_account::guardian::contract::GuardianExecutor::{self, guardianStatusForReturn}; +use alloy::{ + primitives::Address, + providers::Provider, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum GuardianStatus { + /// The guardian does not exist for the account. + DoesNotExist, + /// The guardian exists for the account but is not active. + PresentNotActive, + /// The guardian exists for the account and is active. + Active, +} + +impl GuardianStatus { + pub fn is_present_but_not_active(&self) -> bool { + self == &GuardianStatus::PresentNotActive + } + + pub fn is_active(&self) -> bool { + self == &GuardianStatus::Active + } +} + +impl From for GuardianStatus { + fn from(status: guardianStatusForReturn) -> Self { + match (status.isPresent, status.isActive) { + (true, true) => GuardianStatus::Active, + (true, false) => GuardianStatus::PresentNotActive, + (false, _) => GuardianStatus::DoesNotExist, + } + } +} + +pub async fn get_guardian_status

( + account_address: Address, + guardian_address: Address, + guardian_executor_address: Address, + provider: P, +) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + let guardian_executor = + GuardianExecutor::new(guardian_executor_address, provider); + let status = guardian_executor + .guardianStatusFor(account_address, guardian_address) + .call() + .await?; + Ok(status.into()) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/nonce.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/nonce.rs deleted file mode 100644 index a5c8afef2..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/nonce.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::erc4337::entry_point::EntryPoint; -use alloy::{ - primitives::{Address, U256, Uint}, - providers::Provider, -}; - -pub async fn get_nonce( - entry_point: Address, - account_address: Address, - nonce_key: Uint<192, 3>, - provider: P, -) -> eyre::Result { - let entry_point = EntryPoint::new(entry_point, provider.clone()); - let nonce = entry_point.getNonce(account_address, nonce_key).call().await?; - Ok(nonce) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey.rs new file mode 100644 index 000000000..b7dff31ea --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey.rs @@ -0,0 +1,7 @@ +pub mod active; +pub mod add; +pub mod encoding; +pub mod list; +pub mod remove; + +mod contract; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/active.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/active.rs similarity index 98% rename from packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/active.rs rename to packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/active.rs index 384eff10c..226827fb0 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/active.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/active.rs @@ -1,7 +1,7 @@ use crate::{ config::contracts::Contracts, erc4337::account::modular_smart_account::{ - WebAuthnValidator, + passkey::contract::WebAuthnValidator, utils::{ calculate_from_block, create_logs_filter_with_range, parse_add_remove_events, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/add_passkey.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/add.rs similarity index 68% rename from packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/add_passkey.rs rename to packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/add.rs index 81ddb8cfe..8d43928c1 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/add_passkey.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/add.rs @@ -1,16 +1,17 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - WebAuthnValidator, - send::{SendParams, send_transaction}, + passkey::contract::WebAuthnValidator, + send::{SendUserOpParams, send_user_op}, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes}, providers::Provider, sol, sol_types::SolCall, @@ -27,24 +28,41 @@ sol! { } } -pub async fn add_passkey( - account_address: Address, - passkey: PasskeyPayload, - webauthn_validator: Address, - entry_point_address: Address, - provider: P, - bundler_client: BundlerClient, - signer: Signer, -) -> eyre::Result<()> { +pub struct AddPasskeyParams { + pub account_address: Address, + pub passkey: PasskeyPayload, + pub webauthn_validator: Address, + pub entry_point_address: Address, + pub paymaster: Option, + pub provider: P, + pub bundler_client: BundlerClient, + pub signer: Signer, +} + +pub async fn add_passkey

(params: AddPasskeyParams

) -> eyre::Result<()> +where + P: Provider + Send + Sync + Clone, +{ + let AddPasskeyParams { + account_address, + passkey, + webauthn_validator, + entry_point_address, + paymaster, + provider, + bundler_client, + signer, + } = params; + let call_data = add_passkey_call_data(passkey, webauthn_validator); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -54,21 +72,15 @@ pub async fn add_passkey( Ok(()) } -fn add_passkey_call_data( +pub fn add_passkey_call_data( passkey: PasskeyPayload, webauthn_validator: Address, ) -> Bytes { let add_validation_key_call_data = add_validation_key_call_data(passkey); - - let call = { - let target = webauthn_validator; - let value = U256::from(0); - let data = add_validation_key_call_data; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + encoded_call_with_target_and_data( + webauthn_validator, + add_validation_key_call_data, + ) } fn add_validation_key_call_data(passkey: PasskeyPayload) -> Bytes { @@ -91,9 +103,10 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::{ - add_module::add_module, - module_installed::is_module_installed, + erc7579::module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{IsModuleInstalledParams, is_module_installed}, }, modular_smart_account::{ deploy::{DeployAccountParams, EOASigners, deploy_account}, @@ -156,12 +169,13 @@ mod tests { println!("Account deployed"); - let is_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_module_installed, "Module is not installed"); fund_account_with_default_amount(address, provider.clone()).await?; @@ -172,14 +186,15 @@ mod tests { eoa_validator_address, )?; - add_module( - address, - webauthn_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::webauthn(webauthn_module), entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; }; @@ -202,15 +217,16 @@ mod tests { eoa_validator_address, )?; - add_passkey( - address, + add_passkey(AddPasskeyParams { + account_address: address, passkey, - webauthn_module, + webauthn_validator: webauthn_module, entry_point_address, - provider, - bundler_client, + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/contract.rs new file mode 100644 index 000000000..6e20ffefa --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/contract.rs @@ -0,0 +1,9 @@ +use alloy::sol; + +sol!( + #[derive(Debug, Default)] + #[allow(missing_docs)] + #[sol(rpc)] + WebAuthnValidator, + "../../../../../../packages/erc4337-contracts/out/WebAuthnValidator.sol/WebAuthnValidator.json" +); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/encoding.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/encoding.rs similarity index 100% rename from packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/encoding.rs rename to packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/encoding.rs diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/list.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/list.rs new file mode 100644 index 000000000..6b7a1677b --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/list.rs @@ -0,0 +1,14 @@ +use crate::erc4337::account::modular_smart_account::passkey::contract::WebAuthnValidator; +use alloy::{primitives::Bytes, sol_types::SolCall}; + +pub fn get_account_list_call_data( + domain: String, + credential_id: Vec, +) -> Bytes { + WebAuthnValidator::getAccountListCall { + domain, + credentialId: credential_id.into(), + } + .abi_encode() + .into() +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/remove.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/remove.rs similarity index 70% rename from packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/remove.rs rename to packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/remove.rs index 33220372f..64869697f 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/remove.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/passkey/remove.rs @@ -1,43 +1,59 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - WebAuthnValidator, - send::{SendParams, send_transaction}, - signers::passkey::active::PasskeyDetails, + passkey::{active::PasskeyDetails, contract::WebAuthnValidator}, + send::{SendUserOpParams, send_user_op}, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes}, providers::Provider, sol_types::SolCall, }; +pub struct RemovePasskeyParams { + pub account_address: Address, + pub passkey: PasskeyDetails, + pub entry_point_address: Address, + pub webauthn_validator_address: Address, + pub paymaster: Option, + pub bundler_client: BundlerClient, + pub provider: P, + pub signer: Signer, +} + pub async fn remove_passkey

( - account_address: Address, - passkey: PasskeyDetails, - entry_point_address: Address, - webauthn_validator_address: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, + params: RemovePasskeyParams

, ) -> eyre::Result<()> where P: Provider + Send + Sync + Clone, { + let RemovePasskeyParams { + account_address, + passkey, + entry_point_address, + webauthn_validator_address, + paymaster, + bundler_client, + provider, + signer, + } = params; + let call_data = remove_passkey_call_data(passkey, webauthn_validator_address); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -47,30 +63,20 @@ where Ok(()) } -fn remove_passkey_call_data( +pub fn remove_passkey_call_data( passkey: PasskeyDetails, webauthn_validator_address: Address, ) -> Bytes { let credential_id = passkey.credential_id; let domain = passkey.domain; + let calldata = WebAuthnValidator::removeValidationKeyCall { + credentialId: credential_id, + domain, + } + .abi_encode() + .into(); - let remove_validation_key_calldata = - WebAuthnValidator::removeValidationKeyCall { - credentialId: credential_id, - domain, - } - .abi_encode() - .into(); - - let call = { - let target = webauthn_validator_address; - let value = U256::from(0); - let data = remove_validation_key_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + encoded_call_with_target_and_data(webauthn_validator_address, calldata) } #[cfg(test)] @@ -79,16 +85,16 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::{ - add_module::add_module, - module_installed::is_module_installed, + erc7579::module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{IsModuleInstalledParams, is_module_installed}, }, modular_smart_account::{ - add_passkey::PasskeyPayload, deploy::{DeployAccountParams, EOASigners, deploy_account}, - signers::passkey::{ + passkey::{ active::{PasskeyDetails, get_active_passkeys}, - add::add_passkey, + add::{AddPasskeyParams, PasskeyPayload, add_passkey}, }, test_utilities::fund_account_with_default_amount, }, @@ -152,12 +158,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - account_address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: account_address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_eoa_module_installed, "eoa_module is not installed"); @@ -170,22 +177,24 @@ mod tests { eoa_validator_address, )?; - add_module( + add_module(AddModuleParams { account_address, - webauthn_validator_address, + module: AddModulePayload::webauthn(webauthn_validator_address), entry_point_address, - provider.clone(), - bundler_client.clone(), - signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) .await?; - let is_webauthn_module_installed = is_module_installed( - webauthn_validator_address, - account_address, - provider.clone(), - ) - .await?; + let is_webauthn_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_validator_address), + account: account_address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_webauthn_module_installed, @@ -210,15 +219,16 @@ mod tests { origin_domain: domain_1.clone(), }; - add_passkey( + add_passkey(AddPasskeyParams { account_address, - passkey_payload_1, + passkey: passkey_payload_1, entry_point_address, - webauthn_validator_address, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + webauthn_validator: webauthn_validator_address, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Passkey 1 added"); @@ -241,15 +251,16 @@ mod tests { origin_domain: domain_2.clone(), }; - add_passkey( + add_passkey(AddPasskeyParams { account_address, - passkey_payload_2, + passkey: passkey_payload_2, entry_point_address, - webauthn_validator_address, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + webauthn_validator: webauthn_validator_address, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Passkey 2 added"); @@ -296,15 +307,16 @@ mod tests { domain: domain_1.clone(), }; - remove_passkey( + remove_passkey(RemovePasskeyParams { account_address, - passkey_to_remove, + passkey: passkey_to_remove, entry_point_address, webauthn_validator_address, - bundler_client.clone(), - provider.clone(), + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), signer, - ) + }) .await?; println!("Passkey 1 removed"); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send.rs index 8fa7db866..68589aef5 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send.rs @@ -2,15 +2,17 @@ pub mod eoa; pub mod passkey; use crate::erc4337::{ - account::modular_smart_account::nonce::get_nonce, bundler::{ Bundler, models::receipt::UserOperationReceipt, pimlico::client::BundlerClient, }, - entry_point::EntryPoint, + entry_point::{ + contract::EntryPoint, + nonce::{GetNonceWithKeyParams, get_nonce_with_key}, + }, paymaster::params::{PaymasterParams, build_paymaster_and_data}, signer::Signer, - user_operation::hash::v08::get_user_operation_hash_entry_point, + user_operation::hash::user_operation_hash::get_user_operation_hash_entry_point, }; use alloy::{ primitives::{Address, Bytes, U256, Uint}, @@ -26,7 +28,7 @@ pub struct FactoryPayload { } #[derive(Clone)] -pub struct SendParams { +pub struct SendUserOpParams { pub account: Address, pub entry_point: Address, pub factory_payload: Option, @@ -38,13 +40,13 @@ pub struct SendParams { pub signer: Signer, } -pub async fn send_transaction

( - params: SendParams

, +pub async fn send_user_op

( + params: SendUserOpParams

, ) -> eyre::Result where P: Provider + Send + Sync + Clone, { - let SendParams { + let SendUserOpParams { account, entry_point, factory_payload, @@ -58,7 +60,13 @@ where let nonce_key = nonce_key.unwrap_or_else(|| Uint::from(0)); - let nonce = get_nonce(entry_point, account, nonce_key, &provider).await?; + let nonce = get_nonce_with_key(GetNonceWithKeyParams { + sender: account, + entry_point, + key: nonce_key, + provider: provider.clone(), + }) + .await?; let (estimated_gas, mut user_op) = { let alloy_user_op = { diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/eoa.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/eoa.rs index ffee9eb1f..348196c70 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/eoa.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/eoa.rs @@ -1,5 +1,5 @@ use crate::erc4337::{ - account::modular_smart_account::send::{SendParams, send_transaction}, + account::modular_smart_account::send::{SendUserOpParams, send_user_op}, bundler::pimlico::client::BundlerClient, paymaster::params::PaymasterParams, signer::create_eoa_signer, @@ -39,7 +39,7 @@ pub async fn eoa_send_transaction( let signer = create_eoa_signer(private_key_hex, eoa_validator)?; - _ = send_transaction(SendParams { + _ = send_user_op(SendUserOpParams { account, entry_point, factory_payload: None, @@ -62,8 +62,13 @@ mod tests { erc4337::{ account::{ erc7579::{ - Execution, calls::encode_calls, - module_installed::is_module_installed, + calls::encoded_call_data, + module::{ + Module, + installed::{ + IsModuleInstalledParams, is_module_installed, + }, + }, }, modular_smart_account::{ deploy::{ @@ -84,7 +89,7 @@ mod tests { }, }; use alloy::{ - primitives::{Bytes, U256, address}, + primitives::{U256, address}, providers::ProviderBuilder, }; @@ -134,28 +139,21 @@ mod tests { println!("Account deployed"); - let is_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_module_installed, "Module is not installed"); - let call = { - let target = address; - let value = U256::from(1); - let data = Bytes::default(); - Execution { target, value, data } - }; - - let calls = vec![call]; + let encoded_calls = + encoded_call_data(address, None, Some(U256::from(1))); fund_account_with_default_amount(address, provider.clone()).await?; - let encoded_calls: Bytes = encode_calls(calls).into(); - eoa_send_transaction(EOASendParams { account: address, entry_point: entry_point_address, @@ -271,28 +269,21 @@ mod tests { }) .await?; - let is_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_module_installed, "Module is not installed"); - let call = { - let target = address; - let value = U256::from(1); - let data = Bytes::default(); - Execution { target, value, data } - }; - - let calls = vec![call]; + let encoded_calls = + encoded_call_data(address, None, Some(U256::from(1))); fund_account_with_default_amount(address, provider.clone()).await?; - let encoded_calls: Bytes = encode_calls(calls).into(); - eoa_send_transaction(EOASendParams { account: address, entry_point: entry_point_address, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/passkey.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/passkey.rs index ba69b419d..a4937e29b 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/passkey.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/send/passkey.rs @@ -1,5 +1,5 @@ use crate::erc4337::{ - account::modular_smart_account::send::{SendParams, send_transaction}, + account::modular_smart_account::send::{SendUserOpParams, send_user_op}, bundler::pimlico::client::BundlerClient, paymaster::params::PaymasterParams, signer::{SignatureProvider, Signer}, @@ -43,7 +43,7 @@ where let signer = Signer { provider: signature_provider, stub_signature: stub_sig }; - _ = send_transaction(SendParams { + _ = send_user_op(SendUserOpParams { account, entry_point, factory_payload: None, @@ -66,16 +66,30 @@ pub mod tests { erc4337::{ account::{ erc7579::{ - Execution, add_module::add_module, calls::encode_calls, - module_installed::is_module_installed, + calls::encoded_call_data, + module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{ + IsModuleInstalledParams, is_module_installed, + }, + }, }, modular_smart_account::{ - add_passkey::{PasskeyPayload, add_passkey}, deploy::{DeployAccountParams, EOASigners, deploy_account}, + passkey::add::{ + AddPasskeyParams, PasskeyPayload, add_passkey, + }, test_utilities::fund_account_with_default_amount, }, }, + bundler::Bundler, + entry_point::{ + contract::EntryPoint::PackedUserOperation, + nonce::{GetNonceWithKeyParams, get_nonce_with_key}, + }, signer::{create_eoa_signer, test_utils::get_signature_from_js}, + user_operation::hash::user_operation_hash::get_user_operation_hash_entry_point, }, utils::alloy_utilities::test_utilities::{ TestInfraConfig, @@ -83,8 +97,11 @@ pub mod tests { }, }; use alloy::{ - primitives::{Bytes, U256, address, bytes, fixed_bytes}, - rpc::types::TransactionRequest, + primitives::{Bytes, U256, Uint, address, bytes, fixed_bytes}, + rpc::types::{ + TransactionRequest, + erc4337::PackedUserOperation as AlloyPackedUserOperation, + }, }; use std::sync::Arc; @@ -137,12 +154,13 @@ pub mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_eoa_module_installed, @@ -159,19 +177,24 @@ pub mod tests { )?; { - add_module( - address, - webauthn_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::webauthn(webauthn_module), entry_point_address, - provider.clone(), - bundler_client.clone(), - signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) .await?; let is_web_authn_module_installed = - is_module_installed(webauthn_module, address, provider.clone()) - .await?; + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_web_authn_module_installed, @@ -192,29 +215,22 @@ pub mod tests { let passkey = PasskeyPayload { credential_id, passkey, origin_domain }; - add_passkey( - address, + add_passkey(AddPasskeyParams { + account_address: address, passkey, - webauthn_module, + webauthn_validator: webauthn_module, entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; println!("Passkey successfully added"); // Send transaction using passkey signer - let call = { - let target = address; - let value = U256::from(1); - let data = Bytes::default(); - Execution { target, value, data } - }; - - let calls = vec![call]; - let calldata = encode_calls(calls).into(); + let calldata = encoded_call_data(address, None, Some(U256::from(1))); let signature_provider: SignatureProvider = Arc::new(move |hash: FixedBytes<32>| { @@ -249,12 +265,6 @@ pub mod tests { /// 3. Submit with signed UserOp #[tokio::test] async fn test_send_transaction_webauthn_two_step() -> eyre::Result<()> { - use crate::erc4337::{ - account::modular_smart_account::nonce::get_nonce, bundler::Bundler, - entry_point::EntryPoint::PackedUserOperation, - user_operation::hash::v08::get_user_operation_hash_entry_point, - }; - use alloy::rpc::types::erc4337::PackedUserOperation as AlloyPackedUserOperation; let ( _, anvil_instance, @@ -319,19 +329,24 @@ pub mod tests { // Install WebAuthn validator { - add_module( - address, - webauthn_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::webauthn(webauthn_module), entry_point_address, - provider.clone(), - bundler_client.clone(), - signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) .await?; let is_web_authn_module_installed = - is_module_installed(webauthn_module, address, provider.clone()) - .await?; + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_web_authn_module_installed, @@ -354,15 +369,16 @@ pub mod tests { let passkey_payload = PasskeyPayload { credential_id, passkey, origin_domain }; - add_passkey( - address, - passkey_payload, - webauthn_module, + add_passkey(AddPasskeyParams { + account_address: address, + passkey: passkey_payload, + webauthn_validator: webauthn_module, entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; println!("Passkey successfully added"); @@ -372,20 +388,16 @@ pub mod tests { // Step 1: Build UserOperation and get hash to sign println!("\nStep 1: Building UserOperation..."); - let call = { - let target = address; - let value = U256::from(1); - let data = Bytes::default(); - Execution { target, value, data } - }; - - let calls = vec![call]; - let call_data: Bytes = encode_calls(calls).into(); + let call_data = encoded_call_data(address, None, Some(U256::from(1))); - let nonce_key = alloy::primitives::Uint::from(0); - let nonce = - get_nonce(entry_point_address, address, nonce_key, &provider) - .await?; + let nonce_key = Uint::from(0); + let nonce = get_nonce_with_key(GetNonceWithKeyParams { + sender: address, + entry_point: entry_point_address, + key: nonce_key, + provider: provider.clone(), + }) + .await?; // Create stub signature for gas estimation // Use all-zeros hash to generate a real passkey signature for estimation diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session.rs index 716460a35..7dea7d743 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session.rs @@ -8,14 +8,7 @@ pub mod send; pub mod session_lib; pub mod signature; pub mod signature_wasm; +pub mod state; pub mod status; -use alloy::sol; - -sol!( - #[sol(rpc)] - #[derive(Debug, Default)] - #[allow(missing_docs)] - SessionKeyValidator, - "../../../../../../packages/erc4337-contracts/out/SessionKeyValidator.sol/SessionKeyValidator.json" -); +mod contract; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/active.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/active.rs index 0f2f1b48d..b8bdb3616 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/active.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/active.rs @@ -2,7 +2,8 @@ use crate::{ config::contracts::Contracts, erc4337::account::modular_smart_account::{ session::{ - SessionKeyValidator, session_lib::session_spec::SessionSpec, + contract::SessionKeyValidator, + session_lib::session_spec::SessionSpec, }, utils::{ calculate_from_block, create_logs_filter_with_range, @@ -116,16 +117,17 @@ mod tests { config::contracts::Contracts, erc4337::{ account::{ - erc7579::{ - add_module::add_module, - module_installed::is_module_installed, + erc7579::module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{IsModuleInstalledParams, is_module_installed}, }, modular_smart_account::{ deploy::{DeployAccountParams, EOASigners, deploy_account}, session::{ - create::create_session, + create::{CreateSessionParams, create_session}, hash::hash_session, - revoke::revoke_session, + revoke::{RevokeSessionParams, revoke_session}, session_lib::session_spec::{ limit_type::LimitType, transfer_spec::TransferSpec, usage_limit::UsageLimit, @@ -212,12 +214,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - account_address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: account_address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_eoa_module_installed, @@ -233,22 +236,24 @@ mod tests { eoa_validator_address, )?; - add_module( + add_module(AddModuleParams { account_address, - session_key_module, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - account_address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: account_address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -290,15 +295,16 @@ mod tests { } }; - create_session( + create_session(CreateSessionParams { account_address, - session_spec_1.clone(), + spec: session_spec_1.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Session 1 created"); @@ -330,15 +336,16 @@ mod tests { } }; - create_session( + create_session(CreateSessionParams { account_address, - session_spec_2.clone(), + spec: session_spec_2.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Session 2 created"); @@ -370,30 +377,32 @@ mod tests { } }; - create_session( + create_session(CreateSessionParams { account_address, - session_spec_3.clone(), + spec: session_spec_3.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Session 3 created"); // Revoke session 2 let session_hash_2 = hash_session(session_spec_2.clone()); - revoke_session( + revoke_session(RevokeSessionParams { account_address, - session_hash_2, + session_hash: session_hash_2, entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Session 2 revoked"); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/contract.rs new file mode 100644 index 000000000..90ccce20b --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/contract.rs @@ -0,0 +1,9 @@ +use alloy::sol; + +sol!( + #[sol(rpc)] + #[derive(Debug, Default)] + #[allow(missing_docs)] + SessionKeyValidator, + "../../../../../../packages/erc4337-contracts/out/SessionKeyValidator.sol/SessionKeyValidator.json" +); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/create.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/create.rs index 4f5bd40d6..1475fe6fd 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/create.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/create.rs @@ -1,40 +1,62 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - send::{SendParams, send_transaction}, + send::{SendUserOpParams, send_user_op}, session::{ - SessionKeyValidator, session_lib::session_spec::SessionSpec, + contract::SessionKeyValidator, + session_lib::session_spec::SessionSpec, }, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes}, providers::Provider, sol_types::SolCall, }; -pub async fn create_session( - account_address: Address, - spec: SessionSpec, - entry_point_address: Address, - session_key_validator: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, -) -> eyre::Result<()> { +#[derive(Clone)] +pub struct CreateSessionParams { + pub account_address: Address, + pub spec: SessionSpec, + pub entry_point_address: Address, + pub session_key_validator: Address, + pub paymaster: Option, + pub bundler_client: BundlerClient, + pub provider: P, + pub signer: Signer, +} + +pub async fn create_session

( + params: CreateSessionParams

, +) -> eyre::Result<()> +where + P: Provider + Send + Sync + Clone, +{ + let CreateSessionParams { + account_address, + spec, + entry_point_address, + session_key_validator, + paymaster, + bundler_client, + provider, + signer, + } = params; + let call_data = add_session_call_data(spec, session_key_validator); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -44,24 +66,18 @@ pub async fn create_session( Ok(()) } +pub fn create_session_call_data(spec: SessionSpec) -> Bytes { + SessionKeyValidator::createSessionCall { sessionSpec: spec.into() } + .abi_encode() + .into() +} + fn add_session_call_data( spec: SessionSpec, session_key_validator: Address, ) -> Bytes { - let create_session_calldata = - SessionKeyValidator::createSessionCall { sessionSpec: spec.into() } - .abi_encode() - .into(); - - let call = { - let target = session_key_validator; - let value = U256::from(0); - let data = create_session_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + let calldata = create_session_call_data(spec); + encoded_call_with_target_and_data(session_key_validator, calldata) } #[cfg(test)] @@ -70,16 +86,17 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::{ - add_module::add_module, - module_installed::is_module_installed, + erc7579::module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{IsModuleInstalledParams, is_module_installed}, }, modular_smart_account::{ - add_passkey::PasskeyPayload, deploy::{ DeployAccountParams, EOASigners, WebAuthNSigner, deploy_account, }, + passkey::add::PasskeyPayload, session::session_lib::session_spec::{ limit_type::LimitType, transfer_spec::TransferSpec, usage_limit::UsageLimit, @@ -146,12 +163,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_eoa_module_installed, @@ -166,22 +184,24 @@ mod tests { eoa_validator_address, )?; - add_module( - address, - session_key_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -221,15 +241,16 @@ mod tests { }], }; - create_session( - address, - session_spec, + create_session(CreateSessionParams { + account_address: address, + spec: session_spec, entry_point_address, - session_key_module, + session_key_validator: session_key_module, + paymaster: None, bundler_client, provider, signer, - ) + }) .await?; } @@ -297,12 +318,13 @@ mod tests { println!("Account deployed"); - let is_passkey_module_installed = is_module_installed( - webauthn_validator_address, - address, - provider.clone(), - ) - .await?; + let is_passkey_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_passkey_module_installed, @@ -314,22 +336,24 @@ mod tests { let signer = create_test_webauthn_js_signer(); { - add_module( - address, - session_key_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), - signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -364,15 +388,16 @@ mod tests { }], }; - create_session( - address, - session_spec, + create_session(CreateSessionParams { + account_address: address, + spec: session_spec, entry_point_address, - session_key_module, + session_key_validator: session_key_module, + paymaster: None, bundler_client, provider, - signer.clone(), - ) + signer: signer.clone(), + }) .await?; } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/encode.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/encode.rs index c7fc95ece..d0e21b2ef 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/encode.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/encode.rs @@ -1,7 +1,7 @@ use crate::erc4337::account::{ - erc7579::executeCall, + erc7579::calls::encode_execution_call_data, modular_smart_account::session::{ - SessionLib::SessionSpec as SessionLibSessionSpec, + contract::SessionLib::SessionSpec as SessionLibSessionSpec, session_lib::session_spec::SessionSpec, signature_wasm::get_period_id_no_validation, }, @@ -10,7 +10,6 @@ use alloy::{ dyn_abi::SolType, primitives::{Address, Bytes, FixedBytes, U256, Uint}, sol, - sol_types::SolCall, }; /// Encode execute call data for a session transaction. @@ -36,7 +35,7 @@ pub fn encode_session_user_operation( }; // Pack execution: target (20 bytes) + value (32 bytes) + data (variable) - let execution: Bytes = [ + let execution_call_data: Bytes = [ target.as_slice(), value .as_le_bytes() @@ -50,7 +49,7 @@ pub fn encode_session_user_operation( .concat() .into(); - executeCall { mode, execution }.abi_encode().into() + encode_execution_call_data(mode, execution_call_data) } /// Generate a stub signature for gas estimation. @@ -141,8 +140,8 @@ mod tests { .concat() .into(); - let expected = executeCall { mode, execution }; - let expected_encoded: Bytes = expected.abi_encode().into(); + let expected_encoded: Bytes = + encode_execution_call_data(mode, execution); assert_eq!(encoded, expected_encoded); } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/hash.rs index e0f121571..279d8f6c8 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/hash.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/hash.rs @@ -1,5 +1,5 @@ use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::SessionSpec as SessionLibSessionSpec, + contract::SessionLib::SessionSpec as SessionLibSessionSpec, session_lib::session_spec::SessionSpec, }; use alloy::{ diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/revoke.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/revoke.rs index 6b3536d91..6cfac581c 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/revoke.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/revoke.rs @@ -1,39 +1,57 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - send::{SendParams, send_transaction}, - session::SessionKeyValidator, + send::{SendUserOpParams, send_user_op}, + session::contract::SessionKeyValidator, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, FixedBytes, U256}, + primitives::{Address, Bytes, FixedBytes}, providers::Provider, sol_types::SolCall, }; +#[derive(Clone)] +pub struct RevokeSessionParams { + pub account_address: Address, + pub session_hash: FixedBytes<32>, + pub entry_point_address: Address, + pub session_key_validator: Address, + pub paymaster: Option, + pub bundler_client: BundlerClient, + pub provider: P, + pub signer: Signer, +} + pub async fn revoke_session( - account_address: Address, - session_hash: FixedBytes<32>, - entry_point_address: Address, - session_key_validator: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, + params: RevokeSessionParams

, ) -> eyre::Result<()> { + let RevokeSessionParams { + account_address, + session_hash, + entry_point_address, + session_key_validator, + paymaster, + bundler_client, + provider, + signer, + } = params; + let call_data = revoke_session_call_data(session_hash, session_key_validator); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -52,15 +70,10 @@ fn revoke_session_call_data( .abi_encode() .into(); - let call = { - let target = session_key_validator; - let value = U256::from(0); - let data = create_session_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + encoded_call_with_target_and_data( + session_key_validator, + create_session_calldata, + ) } #[cfg(test)] @@ -69,18 +82,19 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::{ - add_module::add_module, - module_installed::is_module_installed, + erc7579::module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{IsModuleInstalledParams, is_module_installed}, }, modular_smart_account::{ - add_passkey::PasskeyPayload, deploy::{ DeployAccountParams, EOASigners, WebAuthNSigner, deploy_account, }, + passkey::add::PasskeyPayload, session::{ - create::create_session, + create::{CreateSessionParams, create_session}, hash::hash_session, session_lib::session_spec::{ SessionSpec, limit_type::LimitType, @@ -150,12 +164,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_eoa_module_installed, @@ -170,22 +185,24 @@ mod tests { eoa_validator_address, )?; - add_module( - address, - session_key_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -225,15 +242,16 @@ mod tests { }], }; - create_session( - address, - session_spec.clone(), + create_session(CreateSessionParams { + account_address: address, + spec: session_spec.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; session_spec.clone() @@ -253,21 +271,18 @@ mod tests { println!("Session status: {:?}", session_status); - let expected_session_status = 1; - eyre::ensure!( - session_status == expected_session_status, - "Session is not active" - ); + eyre::ensure!(session_status.is_active(), "Session is not active"); - revoke_session( - address, + revoke_session(RevokeSessionParams { + account_address: address, session_hash, entry_point_address, - session_key_module, - bundler_client, - provider, - signer, - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("\n\n\nSession successfully revoked\n\n\n"); @@ -334,12 +349,13 @@ mod tests { println!("Account deployed"); - let is_passkey_module_installed = is_module_installed( - webauthn_validator_address, - address, - provider.clone(), - ) - .await?; + let is_passkey_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_passkey_module_installed, @@ -351,22 +367,24 @@ mod tests { let signer = create_test_webauthn_js_signer(); { - add_module( - address, - session_key_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), - signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: signer.clone(), + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -400,15 +418,16 @@ mod tests { }], }; - create_session( - address, - session_spec.clone(), + create_session(CreateSessionParams { + account_address: address, + spec: session_spec.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("\n\n\nSession successfully created\n\n\n"); @@ -425,24 +444,21 @@ mod tests { println!("Session status: {:?}", session_status); - let expected_session_status = 1; - eyre::ensure!( - session_status == expected_session_status, - "Session is not active" - ); + eyre::ensure!(session_status.is_active(), "Session is not active"); { let session_hash = hash_session(session_spec.clone()); - revoke_session( - address, + revoke_session(RevokeSessionParams { + account_address: address, session_hash, entry_point_address, - session_key_module, - bundler_client, - provider, - signer, - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/send.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/send.rs index d7a5c1cd2..fe6323741 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/send.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/send.rs @@ -12,14 +12,20 @@ mod tests { erc4337::{ account::{ erc7579::{ - Execution, add_module::add_module, calls::encode_calls, - module_installed::is_module_installed, + calls::{Execution, encode_calls, encoded_call_data}, + module::{ + Module, + add::{AddModuleParams, AddModulePayload, add_module}, + installed::{ + IsModuleInstalledParams, is_module_installed, + }, + }, }, modular_smart_account::{ deploy::{DeployAccountParams, EOASigners, deploy_account}, - send::{SendParams, send_transaction}, + send::{SendUserOpParams, send_user_op}, session::{ - create::create_session, + create::{CreateSessionParams, create_session}, session_lib::session_spec::{ SessionSpec, limit_type::LimitType, transfer_spec::TransferSpec, @@ -118,12 +124,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_eoa_module_installed, @@ -138,22 +145,24 @@ mod tests { eoa_validator_address, )?; - add_module( - address, - session_key_module, + add_module(AddModuleParams { + account_address: address, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), signer, - ) + }) .await?; - let is_session_key_module_installed = is_module_installed( - session_key_module, - address, - provider.clone(), - ) - .await?; + let is_session_key_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::session_key_validator(session_key_module), + account: address, + provider: provider.clone(), + }) + .await?; eyre::ensure!( is_session_key_module_installed, @@ -190,28 +199,22 @@ mod tests { }], }; - create_session( - address, - session_spec.clone(), + create_session(CreateSessionParams { + account_address: address, + spec: session_spec.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Session successfully created"); // Send transaction using session signer - let call = { - let value = U256::from(1); - let data = Bytes::default(); - Execution { target, value, data } - }; - - let calls = vec![call]; - let calldata = encode_calls(calls).into(); + let calldata = encoded_call_data(target, None, Some(U256::from(1))); let session_key_hex_owned = session_key_hex.to_string(); let session_spec_arc = Arc::new(session_spec.clone()); @@ -254,7 +257,7 @@ mod tests { let keyed_nonce = keyed_nonce(session_signer_address); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: address, entry_point: entry_point_address, factory_payload: None, @@ -337,14 +340,15 @@ mod tests { signer_private_key.clone(), eoa_validator_address, )?; - add_module( + add_module(AddModuleParams { account_address, - session_key_module, + module: AddModulePayload::session_key(session_key_module), entry_point_address, - provider.clone(), - bundler_client.clone(), - eoa_signer.clone(), - ) + paymaster: None, + provider: provider.clone(), + bundler_client: bundler_client.clone(), + signer: eoa_signer.clone(), + }) .await?; // Create session spec (matches working Rust spec; target == recipient to isolate nonce issue). @@ -370,15 +374,16 @@ mod tests { }], }; - create_session( + create_session(CreateSessionParams { account_address, - session_spec.clone(), + spec: session_spec.clone(), entry_point_address, - session_key_module, - bundler_client.clone(), - provider.clone(), - eoa_signer.clone(), - ) + session_key_validator: session_key_module, + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: eoa_signer.clone(), + }) .await?; // Build session signer (stub signature + provider closure) identical to success case. @@ -412,12 +417,15 @@ mod tests { Signer { provider: signature_provider, stub_signature: stub_sig }; // Prepare a simple value transfer call. - let call = - Execution { target, value: U256::from(1), data: Bytes::default() }; + let call = Execution { + target, + value: U256::from(1), + call_data: Bytes::default(), + }; let calldata = encode_calls(vec![call]).into(); // Intentionally DO NOT provide nonce_key (simulating browser path). Expect failure. - let result = send_transaction(SendParams { + let result = send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec.rs index f70f5fd01..97df5e6d0 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec.rs @@ -6,7 +6,7 @@ pub mod transfer_spec; pub mod usage_limit; use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::{ + contract::SessionLib::{ CallSpec as SessionLibCallSpec, SessionSpec as SessionLibSessionSpec, TransferSpec as SessionLibTransferSpec, UsageLimit as SessionLibUsageLimit, @@ -119,7 +119,7 @@ impl From for Policy { mod tests { use super::*; use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::{ + contract::SessionLib::{ SessionSpec as SessionLibSpec, UsageLimit as SessionLibUsageLimit, }, session_lib::session_spec::{ diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/call_spec.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/call_spec.rs index de376064a..cba1b4e33 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/call_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/call_spec.rs @@ -1,5 +1,5 @@ use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::CallSpec as SessionLibCallSpec, + contract::SessionLib::CallSpec as SessionLibCallSpec, session_lib::session_spec::{ constraint::Constraint, usage_limit::UsageLimit, }, @@ -53,7 +53,7 @@ impl From for SessionLibCallSpec { mod tests { use super::*; use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::{ + contract::SessionLib::{ Constraint as SessionLibConstraint, UsageLimit as SessionLibUsageLimit, }, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/condition.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/condition.rs index d671fc4c4..70f2483b6 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/condition.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/condition.rs @@ -1,4 +1,4 @@ -use crate::erc4337::account::modular_smart_account::session::SessionLib::Condition as SessionLibCondition; +use crate::erc4337::account::modular_smart_account::session::contract::SessionLib::Condition as SessionLibCondition; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/constraint.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/constraint.rs index a625664a0..49307097d 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/constraint.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/constraint.rs @@ -1,5 +1,5 @@ use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::Constraint as SessionLibConstraint, + contract::SessionLib::Constraint as SessionLibConstraint, session_lib::session_spec::{ condition::Condition, usage_limit::UsageLimit, }, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/limit_type.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/limit_type.rs index 8d66e8ca8..bd07c36ea 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/limit_type.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/limit_type.rs @@ -1,4 +1,4 @@ -use crate::erc4337::account::modular_smart_account::session::SessionLib::LimitType as SessionLibLimitType; +use crate::erc4337::account::modular_smart_account::session::contract::SessionLib::LimitType as SessionLibLimitType; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/transfer_spec.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/transfer_spec.rs index 154e48702..f37997f1f 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/transfer_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/transfer_spec.rs @@ -1,5 +1,5 @@ use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::TransferSpec as SessionLibTransferSpec, + contract::SessionLib::TransferSpec as SessionLibTransferSpec, session_lib::session_spec::usage_limit::UsageLimit, }; use alloy::primitives::{Address, U256}; @@ -37,7 +37,7 @@ impl From for SessionLibTransferSpec { mod tests { use super::*; use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::UsageLimit as SessionLibUsageLimit, + contract::SessionLib::UsageLimit as SessionLibUsageLimit, session_lib::session_spec::limit_type::LimitType, }; use alloy::primitives::{Address, U256, address, aliases::U48}; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/usage_limit.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/usage_limit.rs index 1183f79dd..57410d32b 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/usage_limit.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_spec/usage_limit.rs @@ -1,5 +1,5 @@ use crate::erc4337::account::modular_smart_account::session::{ - SessionLib::UsageLimit as SessionLibUsageLimit, + contract::SessionLib::UsageLimit as SessionLibUsageLimit, session_lib::session_spec::limit_type::LimitType, }; use alloy::primitives::{U256, aliases::U48}; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state.rs index f895c0098..23af4924d 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state.rs @@ -3,7 +3,7 @@ pub mod status; use crate::{ erc4337::account::modular_smart_account::session::{ - SessionLib::SessionState as SessionLibSessionState, + contract::SessionLib::SessionState as SessionLibSessionState, session_lib::session_state::{limit_state::LimitState, status::Status}, }, utils::alloy_utilities::serde_helpers::{ diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/limit_state.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/limit_state.rs index 8db7563b8..affcff736 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/limit_state.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/limit_state.rs @@ -1,5 +1,5 @@ use crate::{ - erc4337::account::modular_smart_account::session::SessionLib::LimitState as SessionLibLimitState, + erc4337::account::modular_smart_account::session::contract::SessionLib::LimitState as SessionLibLimitState, utils::alloy_utilities::serde_helpers::{ deserialize_u256_from_integer_string, serialize_u256_as_integer_string, }, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/status.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/status.rs index d40c22107..5f3147623 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/status.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/session_lib/session_state/status.rs @@ -1,4 +1,4 @@ -use crate::erc4337::account::modular_smart_account::session::SessionLib::Status as SessionLibStatus; +use crate::erc4337::account::modular_smart_account::session::contract::SessionLib::Status as SessionLibStatus; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature.rs index cb7243288..5886bf395 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature.rs @@ -1,6 +1,6 @@ use crate::erc4337::account::modular_smart_account::{ session::{ - SessionLib::SessionSpec as SessionLibSessionSpec, + contract::SessionLib::SessionSpec as SessionLibSessionSpec, period_id::get_period_id, session_lib::session_spec::SessionSpec, }, signers::eoa::eoa_sign, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature_wasm.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature_wasm.rs index 83bf58abd..7354bfa03 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature_wasm.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/signature_wasm.rs @@ -1,6 +1,6 @@ use crate::erc4337::account::modular_smart_account::{ session::{ - SessionLib::SessionSpec as SessionLibSessionSpec, + contract::SessionLib::SessionSpec as SessionLibSessionSpec, session_lib::session_spec::{ SessionSpec, limit_type::LimitType, usage_limit::UsageLimit, }, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/state.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/state.rs new file mode 100644 index 000000000..544efc855 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/state.rs @@ -0,0 +1,30 @@ +use crate::erc4337::account::modular_smart_account::session::{ + contract::SessionKeyValidator, + session_lib::{session_spec::SessionSpec, session_state::SessionState}, +}; +use alloy::{ + primitives::{Address, Bytes}, + providers::Provider, + sol_types::SolCall, +}; + +pub async fn get_session_state( + account_address: Address, + session_spec: SessionSpec, + session_key_validator_address: Address, + provider: P, +) -> eyre::Result { + let session_key_validator = + SessionKeyValidator::new(session_key_validator_address, provider); + let state = session_key_validator + .sessionState(account_address, session_spec.into()) + .call() + .await?; + Ok(state.into()) +} + +pub fn session_state_call_data(account: Address, spec: SessionSpec) -> Bytes { + SessionKeyValidator::sessionStateCall { account, spec: spec.into() } + .abi_encode() + .into() +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/status.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/status.rs index a63023dd5..ae80a6c1e 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/status.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/session/status.rs @@ -1,4 +1,6 @@ -use crate::erc4337::account::modular_smart_account::session::SessionKeyValidator; +use crate::erc4337::account::modular_smart_account::session::{ + contract::SessionKeyValidator, session_lib::session_state::status::Status, +}; use alloy::{ primitives::{Address, FixedBytes}, providers::Provider, @@ -9,12 +11,12 @@ pub async fn get_session_status( session_hash: FixedBytes<32>, session_key_validator_address: Address, provider: P, -) -> eyre::Result { +) -> eyre::Result { let session_key_validator = SessionKeyValidator::new(session_key_validator_address, provider); let status = session_key_validator .sessionStatus(account_address, session_hash) .call() .await?; - Ok(status) + status.try_into() } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/add.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/add.rs index 1de59fc75..2c3fa39ef 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/add.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/add.rs @@ -1,41 +1,56 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - send::{SendParams, send_transaction}, + send::{SendUserOpParams, send_user_op}, signers::eoa::EOAKeyValidator, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes}, providers::Provider, sol_types::SolCall, }; -pub async fn add_owner

( - account_address: Address, - new_owner: Address, - entry_point_address: Address, - eoa_validator_address: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, -) -> eyre::Result<()> +pub struct AddOwnerParams { + pub account_address: Address, + pub new_owner: Address, + pub entry_point_address: Address, + pub eoa_validator_address: Address, + pub paymaster: Option, + pub bundler_client: BundlerClient, + pub provider: P, + pub signer: Signer, +} + +pub async fn add_owner

(params: AddOwnerParams

) -> eyre::Result<()> where P: Provider + Send + Sync + Clone, { + let AddOwnerParams { + account_address, + new_owner, + entry_point_address, + eoa_validator_address, + paymaster, + bundler_client, + provider, + signer, + } = params; + let call_data = add_owner_call_data(new_owner, eoa_validator_address); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -52,13 +67,5 @@ fn add_owner_call_data( let add_owner_calldata = EOAKeyValidator::addOwnerCall { owner: new_owner }.abi_encode().into(); - let call = { - let target = eoa_validator_address; - let value = U256::from(0); - let data = add_owner_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + encoded_call_with_target_and_data(eoa_validator_address, add_owner_calldata) } diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/remove.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/remove.rs index 0650d0efa..2552b9088 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/remove.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/eoa/remove.rs @@ -1,41 +1,56 @@ use crate::erc4337::{ account::{ - erc7579::{Execution, calls::encode_calls}, + erc7579::calls::encoded_call_with_target_and_data, modular_smart_account::{ - send::{SendParams, send_transaction}, + send::{SendUserOpParams, send_user_op}, signers::eoa::EOAKeyValidator, }, }, bundler::pimlico::client::BundlerClient, + paymaster::params::PaymasterParams, signer::Signer, }; use alloy::{ - primitives::{Address, Bytes, U256}, + primitives::{Address, Bytes}, providers::Provider, sol_types::SolCall, }; -pub async fn remove_owner

( - account_address: Address, - owner: Address, - entry_point_address: Address, - eoa_validator_address: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, -) -> eyre::Result<()> +pub struct RemoveOwnerParams { + pub account_address: Address, + pub owner: Address, + pub entry_point_address: Address, + pub eoa_validator_address: Address, + pub paymaster: Option, + pub bundler_client: BundlerClient, + pub provider: P, + pub signer: Signer, +} + +pub async fn remove_owner

(params: RemoveOwnerParams

) -> eyre::Result<()> where P: Provider + Send + Sync + Clone, { + let RemoveOwnerParams { + account_address, + owner, + entry_point_address, + eoa_validator_address, + paymaster, + bundler_client, + provider, + signer, + } = params; + let call_data = remove_owner_call_data(owner, eoa_validator_address); - send_transaction(SendParams { + send_user_op(SendUserOpParams { account: account_address, entry_point: entry_point_address, factory_payload: None, call_data, nonce_key: None, - paymaster: None, + paymaster, bundler_client, provider, signer, @@ -52,15 +67,10 @@ fn remove_owner_call_data( let remove_owner_calldata = EOAKeyValidator::removeOwnerCall { owner }.abi_encode().into(); - let call = { - let target = eoa_validator_address; - let value = U256::from(0); - let data = remove_owner_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() + encoded_call_with_target_and_data( + eoa_validator_address, + remove_owner_calldata, + ) } #[cfg(test)] @@ -69,10 +79,16 @@ mod tests { use crate::{ erc4337::{ account::{ - erc7579::module_installed::is_module_installed, + erc7579::module::{ + Module, + installed::{IsModuleInstalledParams, is_module_installed}, + }, modular_smart_account::{ deploy::{DeployAccountParams, EOASigners, deploy_account}, - signers::eoa::{active::get_active_owners, add::add_owner}, + signers::eoa::{ + active::get_active_owners, + add::{AddOwnerParams, add_owner}, + }, test_utilities::fund_account_with_default_amount, }, }, @@ -136,12 +152,13 @@ mod tests { println!("Account deployed"); - let is_eoa_module_installed = is_module_installed( - eoa_validator_address, - account_address, - provider.clone(), - ) - .await?; + let is_eoa_module_installed = + is_module_installed(IsModuleInstalledParams { + module: Module::eoa_validator(eoa_validator_address), + account: account_address, + provider: provider.clone(), + }) + .await?; eyre::ensure!(is_eoa_module_installed, "eoa_module is not installed"); @@ -154,15 +171,16 @@ mod tests { eoa_validator_address, )?; - add_owner( + add_owner(AddOwnerParams { account_address, - owner_to_remove, + new_owner: owner_to_remove, entry_point_address, eoa_validator_address, - bundler_client.clone(), - provider.clone(), - signer.clone(), - ) + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Owner added"); @@ -193,15 +211,16 @@ mod tests { ); // Remove the owner - remove_owner( + remove_owner(RemoveOwnerParams { account_address, - owner_to_remove, + owner: owner_to_remove, entry_point_address, eoa_validator_address, - bundler_client.clone(), - provider.clone(), - signer, - ) + paymaster: None, + bundler_client: bundler_client.clone(), + provider: provider.clone(), + signer: signer.clone(), + }) .await?; println!("Owner removed"); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey.rs index 590a487dc..52595af51 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey.rs @@ -1,8 +1,3 @@ -pub mod active; -pub mod add; -pub mod encoding; -pub mod remove; - use crate::erc4337::account::modular_smart_account::signers::{ STUB_PRIVATE_KEY, eoa::eoa_signature, }; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/add.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/add.rs deleted file mode 100644 index d3fd3842c..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/signers/passkey/add.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::erc4337::{ - account::{ - erc7579::{Execution, calls::encode_calls}, - modular_smart_account::{ - WebAuthnValidator, - add_passkey::PasskeyPayload, - send::{SendParams, send_transaction}, - }, - }, - bundler::pimlico::client::BundlerClient, - signer::Signer, -}; -use alloy::{ - primitives::{Address, Bytes, U256}, - providers::Provider, - sol_types::SolCall, -}; - -pub async fn add_passkey

( - account_address: Address, - passkey: PasskeyPayload, - entry_point_address: Address, - webauthn_validator_address: Address, - bundler_client: BundlerClient, - provider: P, - signer: Signer, -) -> eyre::Result<()> -where - P: Provider + Send + Sync + Clone, -{ - let call_data = add_passkey_call_data(passkey, webauthn_validator_address); - - send_transaction(SendParams { - account: account_address, - entry_point: entry_point_address, - factory_payload: None, - call_data, - nonce_key: None, - paymaster: None, - bundler_client, - provider, - signer, - }) - .await?; - - Ok(()) -} - -fn add_passkey_call_data( - passkey: PasskeyPayload, - webauthn_validator_address: Address, -) -> Bytes { - let credential_id = passkey.credential_id; - let origin_domain = passkey.origin_domain; - let new_key = passkey.passkey; - - let add_validation_key_calldata = WebAuthnValidator::addValidationKeyCall { - credentialId: credential_id, - newKey: new_key, - domain: origin_domain, - } - .abi_encode() - .into(); - - let call = { - let target = webauthn_validator_address; - let value = U256::from(0); - let data = add_validation_key_calldata; - Execution { target, value, data } - }; - - let calls = vec![call]; - encode_calls(calls).into() -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/utils.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/utils.rs index 4301fbbb6..26bac02da 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/utils.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/utils.rs @@ -1,5 +1,6 @@ use alloy::{ primitives::Address, + providers::Provider, rpc::types::{BlockNumberOrTag, Filter, FilterSet, Log}, sol_types::SolEvent, }; @@ -68,3 +69,28 @@ where (added_items, removed_keys) } + +/// Advance the EVM time by the specified number of seconds. +/// This is useful for testing time-dependent functionality like recovery delays. +/// +/// # Arguments +/// * `provider` - The provider connected to an Anvil instance +/// * `seconds` - Number of seconds to advance time by +/// +/// # Returns +/// The new block number after mining (as a hex string) +pub async fn advance_time

(provider: &P, seconds: u64) -> eyre::Result +where + P: Provider + Send + Sync + Clone, +{ + // Increase time by the specified number of seconds + let _: u64 = + provider.raw_request("evm_increaseTime".into(), (seconds,)).await?; + + // Mine a block to apply the time change + // evm_mine returns the new block number as a hex string + let block_number: String = + provider.raw_request("evm_mine".into(), ()).await?; + + Ok(block_number) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/client.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/client.rs deleted file mode 100644 index cd10f488d..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/client.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::{ - config::Config, - erc4337::{send_call::SendCalls, signature::sign_user_operation_dummy}, - result::Result, -}; -use alloy::{ - primitives::Bytes, - providers::{Provider, ext::Erc4337Api}, - rpc::types::erc4337::SendUserOperation, - signers::local::PrivateKeySigner, -}; - -#[cfg(any(test, feature = "test-utilities"))] -pub mod alto_test_utils; - -#[derive(Debug, Clone)] -pub struct Client

{ - pub config: Config, - pub signer: PrivateKeySigner, - pub provider: P, -} - -impl

Client

-where - P: Provider + Clone, -{ - pub fn new(config: Config, signer: PrivateKeySigner, provider: P) -> Self { - Self { config, signer, provider } - } - - pub async fn send_user_operation(&self, req: SendCalls) -> Result { - let signed = sign_user_operation_dummy(&self.config, &self.signer, req); - - let alloy_packed = signed.packed.into(); - - let resp = self - .provider - .send_user_operation( - SendUserOperation::EntryPointV07(alloy_packed), - self.config.contracts.entry_point, - ) - .await - .map_err(|e| { - crate::error::ZkSyncSsoError::SendUserOperation(e.to_string()) - })?; - - Ok(resp.user_op_hash) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - chain::{Chain, id::ChainId}, - config::contracts::Contracts, - erc4337::entry_point::version::EntryPointVersion, - }; - use alloy::{ - primitives::{Address, Bytes, U256}, - providers::ProviderBuilder, - signers::local::PrivateKeySigner, - }; - use std::str::FromStr; - - fn create_test_config() -> Config { - let contracts = Contracts::new( - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap(), - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap(), - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap(), - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap(), - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap(), - ); - - let chain = Chain::new( - ChainId::ETHEREUM_MAINNET, - EntryPointVersion::V08, - "Mainnet".to_string(), - ); - - Config::new( - "http://localhost:8545".parse().unwrap(), - "http://localhost:4337".parse().unwrap(), - chain, - contracts, - ) - } - - fn create_test_signer() -> PrivateKeySigner { - let private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; - PrivateKeySigner::from_str(private_key).unwrap() - } - - fn create_test_send_calls() -> SendCalls { - SendCalls { - account: Address::from_str( - "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - ) - .unwrap(), - calls: vec![crate::erc4337::send_call::Call { - to: Address::from_str( - "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", - ) - .unwrap(), - data: Bytes::from_static(b"test_data"), - value: U256::from(1000), - }], - } - } - - #[tokio::test] - async fn test_client_creation() { - let provider = ProviderBuilder::new().connect_anvil_with_wallet(); - let config = create_test_config(); - let signer = create_test_signer(); - - let client = Client::new(config, signer, provider); - - assert_eq!( - client.config.contracts.entry_point, - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") - .unwrap() - ); - } - - #[tokio::test] - async fn test_send_user_operation() { - let provider = ProviderBuilder::new().connect_anvil_with_wallet(); - let config = create_test_config(); - let signer = create_test_signer(); - let send_calls = create_test_send_calls(); - - let client = Client::new(config, signer, provider); - let result = client.send_user_operation(send_calls).await; - assert!(result.is_err()); - - match result { - Err(crate::error::ZkSyncSsoError::SendUserOperation(_)) => { - // This is expected with dummy signature - } - _ => panic!("Expected SendUserOperation error, got: {:?}", result), - } - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point.rs index 503f1b9a9..ef136d51c 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point.rs @@ -1,47 +1,9 @@ -use alloy::sol; -use std::fmt::Debug; - pub mod config; pub mod nonce; pub mod sender_address; +pub mod user_op_hash; pub mod version; -sol!( - #[sol(rpc)] - EntryPoint, - "../../../../../../packages/erc4337-contracts/out/EntryPoint.sol/EntryPoint.json" -); - -pub use self::EntryPoint::PackedUserOperation; - -impl Debug for PackedUserOperation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PackedUserOperation") - .field("sender", &self.sender) - .field("nonce", &self.nonce) - .field("initCode", &self.initCode) - .field("callData", &self.callData) - .field("accountGasLimits", &self.accountGasLimits) - .field("preVerificationGas", &self.preVerificationGas) - .field("gasFees", &self.gasFees) - .field("paymasterAndData", &self.paymasterAndData) - .field("signature", &self.signature) - .finish() - } -} - -impl PartialEq for PackedUserOperation { - fn eq(&self, other: &Self) -> bool { - self.sender == other.sender - && self.nonce == other.nonce - && self.initCode == other.initCode - && self.callData == other.callData - && self.accountGasLimits == other.accountGasLimits - && self.preVerificationGas == other.preVerificationGas - && self.gasFees == other.gasFees - && self.paymasterAndData == other.paymasterAndData - && self.signature == other.signature - } -} +pub(crate) mod contract; -impl Eq for PackedUserOperation {} +pub use contract::PackedUserOperation; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/contract.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/contract.rs new file mode 100644 index 000000000..fc87e6099 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/contract.rs @@ -0,0 +1,42 @@ +use alloy::sol; +use std::fmt::Debug; + +sol!( + #[sol(rpc)] + EntryPoint, + "../../../../../../packages/erc4337-contracts/out/EntryPoint.sol/EntryPoint.json" +); + +pub use self::EntryPoint::PackedUserOperation; + +impl Debug for PackedUserOperation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PackedUserOperation") + .field("sender", &self.sender) + .field("nonce", &self.nonce) + .field("initCode", &self.initCode) + .field("callData", &self.callData) + .field("accountGasLimits", &self.accountGasLimits) + .field("preVerificationGas", &self.preVerificationGas) + .field("gasFees", &self.gasFees) + .field("paymasterAndData", &self.paymasterAndData) + .field("signature", &self.signature) + .finish() + } +} + +impl PartialEq for PackedUserOperation { + fn eq(&self, other: &Self) -> bool { + self.sender == other.sender + && self.nonce == other.nonce + && self.initCode == other.initCode + && self.callData == other.callData + && self.accountGasLimits == other.accountGasLimits + && self.preVerificationGas == other.preVerificationGas + && self.gasFees == other.gasFees + && self.paymasterAndData == other.paymasterAndData + && self.signature == other.signature + } +} + +impl Eq for PackedUserOperation {} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/nonce.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/nonce.rs index 6a2ee5745..ed5cabd0f 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/nonce.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/nonce.rs @@ -1,30 +1,48 @@ -use crate::erc4337::entry_point::EntryPoint; +use crate::erc4337::entry_point::contract::EntryPoint; use alloy::{ contract::{Error, private::Provider}, - primitives::{Address, U256, aliases::U192}, + primitives::{Address, Bytes, U256, aliases::U192}, + sol_types::SolCall, }; pub async fn get_nonce

( - provider: &P, - address: Address, - entry_point_address: &Address, + sender: Address, + entry_point: Address, + provider: P, ) -> Result where - P: Provider, + P: Provider + Send + Sync + Clone, { - get_nonce_with_key(provider, address, entry_point_address, U192::ZERO).await + get_nonce_with_key(GetNonceWithKeyParams { + sender, + entry_point, + key: U192::ZERO, + provider, + }) + .await +} + +#[derive(Clone)] +pub struct GetNonceWithKeyParams { + pub sender: Address, + pub entry_point: Address, + pub key: U192, + pub provider: P, } pub async fn get_nonce_with_key

( - provider: &P, - address: Address, - entry_point_address: &Address, - key: U192, + params: GetNonceWithKeyParams

, ) -> Result where - P: Provider, + P: Provider + Send + Sync + Clone, { - let entry_point_instance = EntryPoint::new(*entry_point_address, provider); - let nonce = entry_point_instance.getNonce(address, key).call().await?; + let GetNonceWithKeyParams { sender, entry_point, key, provider } = params; + let entry_point_instance = EntryPoint::new(entry_point, provider); + let nonce = entry_point_instance.getNonce(sender, key).call().await?; Ok(nonce) } + +pub fn get_nonce_call_data(sender: Address, key: U192) -> Bytes { + let call = EntryPoint::getNonceCall { sender, key }; + call.abi_encode().into() +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/sender_address.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/sender_address.rs index 4bfbf941e..3d698b358 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/sender_address.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/sender_address.rs @@ -1,4 +1,4 @@ -use crate::erc4337::entry_point::{ +use crate::erc4337::entry_point::contract::{ EntryPoint, EntryPoint::{ SenderAddressResult, getSenderAddressCall, getSenderAddressReturn, diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/user_op_hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/user_op_hash.rs new file mode 100644 index 000000000..4fd5ab5ba --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/entry_point/user_op_hash.rs @@ -0,0 +1,11 @@ +use crate::erc4337::entry_point::contract::{ + EntryPoint::getUserOpHashCall, PackedUserOperation, +}; +use alloy::{primitives::Bytes, sol_types::SolCall}; + +pub fn get_user_op_hash_call_data( + user_operation: PackedUserOperation, +) -> eyre::Result { + let call = getUserOpHashCall { userOp: user_operation }; + Ok(call.abi_encode().into()) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/client.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/client.rs index f8af1ec54..c859fb409 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/client.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/client.rs @@ -2,16 +2,7 @@ use crate::{ erc4337::{ bundler::config::BundlerConfig, paymaster::pimlico::models::{ - v07::{ - SponsorshipResponse as SponsorshipResponseV07, - SponsorshipResult as SponsorshipResultV07, - UserOperationPreSponsorship as UserOperationPreSponsorshipV07, - }, - v08::{ - SponsorshipResponse as SponsorshipResponseV08, - SponsorshipResult as SponsorshipResultV08, - UserOperationPreSponsorship as UserOperationPreSponsorshipV08, - }, + SponsorshipResponse, SponsorshipResult, UserOperationPreSponsorship, }, }, jsonrpc::{JSONRPCResponse, Request, Response}, @@ -29,75 +20,12 @@ impl PaymasterClient { Self { client: reqwest::Client::new(), config } } - pub async fn sponsor_user_operation_v07( + pub async fn sponsor_user_operation( &self, - user_operation: &UserOperationPreSponsorshipV07, + user_operation: &UserOperationPreSponsorship, entry_point: &Address, sponsorship_policy_id: Option, - ) -> eyre::Result { - println!("sponsor_user_operation_v07 "); - - let bundler_url = self.config.url().clone(); - - let params: Vec = { - let user_operation_value = serde_json::to_value(user_operation)?; - let mut vec: Vec = - vec![user_operation_value, entry_point.to_string().into()]; - if let Some(sponsorship_policy_id) = sponsorship_policy_id { - vec.push(sponsorship_policy_id.into()); - } - vec - }; - - let req_body: Request> = Request { - jsonrpc: "2.0".into(), - id: 1, - method: "pm_sponsorUserOperation".into(), - params, - }; - println!("req_body: {:?}", serde_json::to_string(&req_body)?); - - let post = self - .client - .post(bundler_url.as_str()) - .json(&req_body) - .send() - .await?; - println!("pm_sponsorUserOperation post: {:?}", post); - let res = post.text().await?; - println!("pm_sponsorUserOperation res: {:?}", res); - let v = serde_json::from_str::>( - &res, - )?; - - println!("pm_sponsorUserOperation json: {:?}", v); - - let response: Response = v.into(); - - let response_estimate = response?; - let response_estimate = response_estimate.unwrap(); - - let result = SponsorshipResultV07 { - call_gas_limit: response_estimate.call_gas_limit, - verification_gas_limit: response_estimate.verification_gas_limit, - pre_verification_gas: response_estimate.pre_verification_gas, - paymaster: response_estimate.paymaster, - paymaster_verification_gas_limit: response_estimate - .paymaster_verification_gas_limit, - paymaster_post_op_gas_limit: response_estimate - .paymaster_post_op_gas_limit, - paymaster_data: response_estimate.paymaster_data, - }; - - Ok(result) - } - - pub async fn sponsor_user_operation_v08( - &self, - user_operation: &UserOperationPreSponsorshipV08, - entry_point: &Address, - sponsorship_policy_id: Option, - ) -> eyre::Result { + ) -> eyre::Result { println!("sponsor_user_operation_v08"); let bundler_url = self.config.url(); @@ -131,18 +59,17 @@ impl PaymasterClient { println!("pm_sponsorUserOperation post: {:?}", post); let res = post.text().await?; println!("pm_sponsorUserOperation res: {:?}", res); - let v = serde_json::from_str::>( - &res, - )?; + let v = + serde_json::from_str::>(&res)?; println!("pm_sponsorUserOperation json: {:?}", v); - let response: Response = v.into(); + let response: Response = v.into(); let response_estimate = response?; let response_estimate = response_estimate.unwrap(); - let result = SponsorshipResultV08 { + let result = SponsorshipResult { call_gas_limit: response_estimate.call_gas_limit, verification_gas_limit: response_estimate.verification_gas_limit, pre_verification_gas: response_estimate.pre_verification_gas, @@ -157,83 +84,3 @@ impl PaymasterClient { Ok(result) } } - -#[cfg(test)] -mod tests { - use super::*; - use alloy::{ - primitives::Address, - rpc::types::erc4337::PackedUserOperation as AlloyPackedUserOperation, - }; - use eyre::ensure; - - pub async fn setup_sponsor_user_operation_v07_paymaster_mock() - -> eyre::Result { - use wiremock::{ - Mock, MockServer, ResponseTemplate, - matchers::{method, path}, - }; - - let mock_server = MockServer::start().await; - - let url = mock_server.uri().to_string(); - - let expected_request_body = serde_json::json!({ - "id": 1, - "jsonrpc": "2.0", - "method": "pm_sponsorUserOperation", - }); - - let sponsorship_payload = SponsorshipResponseV07::mock(); - - let response_body = serde_json::json!({ - "id": 1, - "jsonrpc": "2.0", - "result": sponsorship_payload, - }); - - let response = ResponseTemplate::new(200).set_body_json(response_body); - - use wiremock::matchers::body_partial_json; - - Mock::given(method("POST")) - .and(path("/")) - .and(body_partial_json(&expected_request_body)) - .respond_with(response) - .mount(&mock_server) - .await; - - let bundler_client = PaymasterClient::new(BundlerConfig::new(url)); - - Ok(bundler_client) - } - - #[tokio::test] - #[ignore = "not yet implemented"] - async fn test_sponsor_user_operation_v07() -> eyre::Result<()> { - let paymaster_client = - setup_sponsor_user_operation_v07_paymaster_mock().await?; - - let entry_point = - "0x0000000071727De22E5E9d8BAf0edAc6f37da032".parse::

()?; - let entry_point_address = entry_point; - - let user_operation_entry_point = crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07::mock().0; - let user_operation_alloy: AlloyPackedUserOperation = - user_operation_entry_point.into(); - let user_operation_pre = - UserOperationPreSponsorshipV07::from(user_operation_alloy); - - let sponsorship_result = paymaster_client - .sponsor_user_operation_v07( - &user_operation_pre, - &entry_point_address, - None, - ) - .await?; - - ensure!(sponsorship_result.call_gas_limit.to_string() == "100000"); - - Ok(()) - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models.rs index 237868161..68c034b11 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models.rs @@ -1,2 +1,95 @@ -pub mod v07; -pub mod v08; +use crate::erc4337::entry_point::contract::PackedUserOperation; +use alloy::primitives::{Address, Bytes, U256}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug)] +pub struct UserOperationPreSponsorship(pub PackedUserOperation); + +impl From for UserOperationPreSponsorship { + fn from(op: PackedUserOperation) -> Self { + Self(op) + } +} + +impl Serialize for UserOperationPreSponsorship { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + let mut state = + serializer.serialize_struct("PackedUserOperation", 9)?; + state.serialize_field("sender", &self.0.sender)?; + state.serialize_field("nonce", &self.0.nonce)?; + state.serialize_field("initCode", &self.0.initCode)?; + state.serialize_field("callData", &self.0.callData)?; + state.serialize_field("accountGasLimits", &self.0.accountGasLimits)?; + state.serialize_field( + "preVerificationGas", + &self.0.preVerificationGas, + )?; + state.serialize_field("gasFees", &self.0.gasFees)?; + state.serialize_field("paymasterAndData", &self.0.paymasterAndData)?; + state.serialize_field("signature", &self.0.signature)?; + state.end() + } +} + +#[derive( + Default, + Clone, + Debug, + Ord, + PartialOrd, + PartialEq, + Eq, + Serialize, + Deserialize, +)] +#[serde(rename_all = "camelCase")] +pub struct SponsorshipResult { + pub call_gas_limit: U256, + pub verification_gas_limit: U256, + pub pre_verification_gas: U256, + pub paymaster: Address, + pub paymaster_verification_gas_limit: U256, + pub paymaster_post_op_gas_limit: U256, + pub paymaster_data: Bytes, +} + +#[derive( + Default, + Clone, + Debug, + Ord, + PartialOrd, + PartialEq, + Eq, + Serialize, + Deserialize, +)] +#[serde(rename_all = "camelCase")] +pub struct SponsorshipResponse { + pub pre_verification_gas: U256, + pub verification_gas_limit: U256, + pub call_gas_limit: U256, + pub paymaster: Address, + pub paymaster_verification_gas_limit: U256, + pub paymaster_post_op_gas_limit: U256, + pub paymaster_data: Bytes, +} + +impl SponsorshipResponse { + pub fn mock() -> Self { + Self { + call_gas_limit: U256::from(100000), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(50000), + paymaster: Address::ZERO, + paymaster_verification_gas_limit: U256::from(50000), + paymaster_post_op_gas_limit: U256::from(50000), + paymaster_data: Bytes::default(), + } + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v07.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v07.rs deleted file mode 100644 index 19164dbec..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v07.rs +++ /dev/null @@ -1,121 +0,0 @@ -use alloy::{ - primitives::{Address, Bytes, U256}, - rpc::types::erc4337::PackedUserOperation, -}; -use serde::{Deserialize, Serialize}; - -#[derive( - Default, - Clone, - Debug, - Ord, - PartialOrd, - PartialEq, - Eq, - Serialize, - Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct UserOperationPreSponsorship { - pub sender: Address, - pub nonce: U256, - #[serde(skip_serializing_if = "Option::is_none")] - pub factory: Option
, - #[serde(skip_serializing_if = "Option::is_none")] - pub factory_data: Option, - pub call_data: Bytes, - pub call_gas_limit: U256, - pub verification_gas_limit: U256, - pub pre_verification_gas: U256, - pub max_fee_per_gas: U256, - pub max_priority_fee_per_gas: U256, - pub paymaster: Option
, - pub paymaster_verification_gas_limit: Option, - pub paymaster_post_op_gas_limit: Option, - pub paymaster_data: Option, - // #[serde(skip_serializing_if = "Option::is_none")] - // pub authorization_list: Option>, - pub signature: Bytes, -} - -impl From for UserOperationPreSponsorship { - fn from(user_op: PackedUserOperation) -> Self { - Self { - sender: user_op.sender, - nonce: user_op.nonce, - factory: user_op.factory, - factory_data: user_op.factory_data, - call_data: user_op.call_data, - call_gas_limit: user_op.call_gas_limit, - verification_gas_limit: user_op.verification_gas_limit, - pre_verification_gas: user_op.pre_verification_gas, - max_fee_per_gas: user_op.max_fee_per_gas, - max_priority_fee_per_gas: user_op.max_priority_fee_per_gas, - paymaster: user_op.paymaster, - paymaster_verification_gas_limit: user_op - .paymaster_verification_gas_limit, - paymaster_post_op_gas_limit: user_op.paymaster_post_op_gas_limit, - paymaster_data: user_op.paymaster_data, - // authorization_list: user_op.authorization_list, - signature: user_op.signature, - } - } -} - -#[derive( - Default, - Clone, - Debug, - Ord, - PartialOrd, - PartialEq, - Eq, - Serialize, - Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct SponsorshipResult { - pub call_gas_limit: U256, - pub verification_gas_limit: U256, - pub pre_verification_gas: U256, - pub paymaster: Address, - pub paymaster_verification_gas_limit: U256, - pub paymaster_post_op_gas_limit: U256, - pub paymaster_data: Bytes, -} - -#[derive( - Default, - Clone, - Debug, - Ord, - PartialOrd, - PartialEq, - Eq, - Serialize, - Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct SponsorshipResponse { - pub pre_verification_gas: U256, - pub verification_gas_limit: U256, - pub call_gas_limit: U256, - pub paymaster: Address, - pub paymaster_verification_gas_limit: U256, - pub paymaster_post_op_gas_limit: U256, - pub paymaster_data: Bytes, -} - -impl SponsorshipResponse { - pub fn mock() -> Self { - Self { - call_gas_limit: U256::from(100000), - verification_gas_limit: U256::from(100000), - pre_verification_gas: U256::from(50000), - paymaster: Address::ZERO, - paymaster_verification_gas_limit: U256::from(50000), - paymaster_post_op_gas_limit: U256::from(50000), - paymaster_data: Bytes::default(), - } - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v08.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v08.rs deleted file mode 100644 index 6b6419650..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/paymaster/pimlico/models/v08.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{Address, Bytes, U256}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug)] -pub struct UserOperationPreSponsorship(pub PackedUserOperation); - -impl From for UserOperationPreSponsorship { - fn from(op: PackedUserOperation) -> Self { - Self(op) - } -} - -impl Serialize for UserOperationPreSponsorship { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - - let mut state = - serializer.serialize_struct("PackedUserOperation", 9)?; - state.serialize_field("sender", &self.0.sender)?; - state.serialize_field("nonce", &self.0.nonce)?; - state.serialize_field("initCode", &self.0.initCode)?; - state.serialize_field("callData", &self.0.callData)?; - state.serialize_field("accountGasLimits", &self.0.accountGasLimits)?; - state.serialize_field( - "preVerificationGas", - &self.0.preVerificationGas, - )?; - state.serialize_field("gasFees", &self.0.gasFees)?; - state.serialize_field("paymasterAndData", &self.0.paymasterAndData)?; - state.serialize_field("signature", &self.0.signature)?; - state.end() - } -} - -#[derive( - Default, - Clone, - Debug, - Ord, - PartialOrd, - PartialEq, - Eq, - Serialize, - Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct SponsorshipResult { - pub call_gas_limit: U256, - pub verification_gas_limit: U256, - pub pre_verification_gas: U256, - pub paymaster: Address, - pub paymaster_verification_gas_limit: U256, - pub paymaster_post_op_gas_limit: U256, - pub paymaster_data: Bytes, -} - -#[derive( - Default, - Clone, - Debug, - Ord, - PartialOrd, - PartialEq, - Eq, - Serialize, - Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct SponsorshipResponse { - pub pre_verification_gas: U256, - pub verification_gas_limit: U256, - pub call_gas_limit: U256, - pub paymaster: Address, - pub paymaster_verification_gas_limit: U256, - pub paymaster_post_op_gas_limit: U256, - pub paymaster_data: Bytes, -} - -impl SponsorshipResponse { - pub fn mock() -> Self { - Self { - call_gas_limit: U256::from(100000), - verification_gas_limit: U256::from(100000), - pre_verification_gas: U256::from(50000), - paymaster: Address::ZERO, - paymaster_verification_gas_limit: U256::from(50000), - paymaster_post_op_gas_limit: U256::from(50000), - paymaster_data: Bytes::default(), - } - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signature.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signature.rs deleted file mode 100644 index e58a4f539..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signature.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::user_operation::wrapper_v07::create_user_operation; -use crate::{ - config::Config, - erc4337::{entry_point::PackedUserOperation, send_call::SendCalls}, -}; -use alloy::{primitives::Bytes, signers::local::PrivateKeySigner}; - -pub struct SignedUserOperation { - pub packed: PackedUserOperation, - pub user_op_hash: Bytes, -} - -pub fn sign_user_operation_dummy( - _config: &Config, - _signer: &PrivateKeySigner, - _op: SendCalls, -) -> SignedUserOperation { - SignedUserOperation { - packed: create_user_operation(), - user_op_hash: Bytes::from_static(b"dummy_user_op_hash"), - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signer.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signer.rs index d6dc3273a..f74c5ef2c 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signer.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/signer.rs @@ -1,18 +1,10 @@ #[cfg(any(test, feature = "test-utilities"))] pub mod test_utils; -use crate::erc4337::{ - account::modular_smart_account::signers::eoa::{ - eoa_signature, stub_signature_eoa, - }, - entry_point::PackedUserOperation, - user_operation::wrapper_v07::PackedUserOperationWrapperV07, -}; -use alloy::{ - hex, - primitives::{Address, Bytes, FixedBytes}, - signers::{SignerSync, local::PrivateKeySigner}, +use crate::erc4337::account::modular_smart_account::signers::eoa::{ + eoa_signature, stub_signature_eoa, }; +use alloy::primitives::{Address, Bytes, FixedBytes}; use eyre; use std::{future::Future, pin::Pin, sync::Arc}; @@ -58,32 +50,3 @@ pub fn create_eoa_signer( Ok(Signer { stub_signature: stub_sig, provider: signature_provider }) } - -pub fn sign_user_operation_v07_with_ecdsa( - uo: &PackedUserOperation, - ep: &Address, - chain_id: u64, - signer: PrivateKeySigner, -) -> eyre::Result { - let uo_wrapper = PackedUserOperationWrapperV07::from(uo.clone()); - let hash = uo_wrapper.hash(ep, chain_id); - - println!("hash: {:?}", hash.clone()); - - let message = hash.0; - - println!("message: {:?}", message.clone()); - - let message_bytes = message.to_vec(); - - println!("message_bytes: {:?}", message_bytes.clone()); - - let signature = signer.sign_message_sync(&message_bytes)?; - println!("signature: {:?}", signature); - let sig_vec: Vec = signature.into(); - println!("hex::encode(sig_vec): {:?}", hex::encode(sig_vec.clone())); - - let mut user_operation = uo.clone(); - user_operation.signature = sig_vec.into(); - Ok(user_operation) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation.rs index a5460dba9..3ac1d10b0 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation.rs @@ -1,17 +1,15 @@ -use crate::erc4337::entry_point::PackedUserOperation; +use crate::erc4337::entry_point::contract::PackedUserOperation; use alloy::primitives::{Address, Bytes, FixedBytes, U256}; use serde::{Deserialize, Serialize}; -pub mod alloy_helpers; -pub mod call; -pub mod estimated; pub mod hash; -pub mod signature; -pub mod signed; -#[cfg(test)] -pub mod tests; -pub mod utils; -pub mod wrapper_v07; + +pub(crate) mod alloy_helpers; +pub(crate) mod call; +pub(crate) mod estimated; +pub(crate) mod signature; +pub(crate) mod signed; +pub(crate) mod utils; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SignedAuthorization { diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/alloy_helpers.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/alloy_helpers.rs index 57fb46d0b..ae41977ee 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/alloy_helpers.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/alloy_helpers.rs @@ -1,4 +1,7 @@ -use crate::erc4337::user_operation::PackedUserOperation; +use crate::erc4337::{ + entry_point::contract::PackedUserOperation as EntryPointPackedUserOperation, + user_operation::PackedUserOperation, +}; use alloy::{ primitives::{Address, Bytes, U256}, rpc::types::erc4337::PackedUserOperation as AlloyPackedUserOperation, @@ -12,7 +15,7 @@ impl From for AlloyPackedUserOperation { // Convert from our entry_point::PackedUserOperation to alloy's PackedUserOperation fn convert_to_alloy_packed_user_op( - op: &crate::erc4337::entry_point::PackedUserOperation, + op: &EntryPointPackedUserOperation, ) -> AlloyPackedUserOperation { // Extract factory and factory_data from initCode let (factory, factory_data) = if op.initCode.len() >= 20 { diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/estimated.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/estimated.rs index e9c818525..03833f249 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/estimated.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/estimated.rs @@ -1,4 +1,4 @@ -use crate::erc4337::entry_point::PackedUserOperation; +use crate::erc4337::entry_point::contract::PackedUserOperation; #[derive(Debug, Clone, PartialEq, Eq)] pub struct UserOperationEstimated(PackedUserOperation); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash.rs index c4dc6c736..ee21e780a 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash.rs @@ -1,9 +1,6 @@ use core::fmt; -pub mod combine; pub mod user_operation_hash; -pub mod v07; -pub mod v08; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct SentUserOperationHash(String); diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/combine.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/combine.rs deleted file mode 100644 index 9a1860654..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/combine.rs +++ /dev/null @@ -1,25 +0,0 @@ -use alloy::primitives::{B256, U256}; - -pub fn combine_and_trim_first_16_bytes(item1: U256, item2: U256) -> B256 { - let mut vec = Vec::with_capacity(32); - vec.extend_from_slice(&item1.to_be_bytes_vec()[16..]); - vec.extend_from_slice(&item2.to_be_bytes_vec()[16..]); - B256::from_slice(&vec) -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy::primitives::b256; - - #[test] - fn test_combine_and_trim_first_16_bytes() { - let expected_result = b256!( - "0000000000000000000000000000000100000000000000000000000000000002" - ); - let item1 = U256::from(1); - let item2 = U256::from(2); - let result = combine_and_trim_first_16_bytes(item1, item2); - assert_eq!(result, expected_result); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/user_operation_hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/user_operation_hash.rs index f43b26738..3be1a15a3 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/user_operation_hash.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/user_operation_hash.rs @@ -1,4 +1,8 @@ -use alloy::primitives::B256; +use crate::erc4337::entry_point::contract::{EntryPoint, PackedUserOperation}; +use alloy::{ + primitives::{Address, B256}, + providers::Provider, +}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -74,3 +78,13 @@ impl UserOperationHash { ret } } + +pub async fn get_user_operation_hash_entry_point( + user_operation: &PackedUserOperation, + entry_point: &Address, + provider: P, +) -> eyre::Result { + let entry_point = EntryPoint::new(*entry_point, provider.clone()); + let hash = entry_point.getUserOpHash(user_operation.clone()).call().await?; + Ok(hash.into()) +} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07.rs deleted file mode 100644 index 30719fc07..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::{ - user_operation_hash::UserOperationHash, v07::pack::pack_user_operation, - }, -}; -use alloy::{ - primitives::{Address, B256, Bytes, U256, keccak256}, - sol_types::SolValue, -}; - -pub mod pack; - -pub fn get_user_operation_hash( - user_operation: &PackedUserOperation, - entry_point: &Address, - chain_id: u64, -) -> UserOperationHash { - let packed_user_operation = { - let packed = pack_user_operation(user_operation); - println!("packed: {:?}", packed); - keccak256(packed) - }; - println!("packed_user_operation: {:?}", packed_user_operation); - - let chain_id = U256::from(chain_id); - - let values = (packed_user_operation, entry_point, chain_id); - let abi_encoded = values.abi_encode(); - let abi_encoded_packed = values.abi_encode_packed(); - println!("abi_encoded: {:?}", abi_encoded.clone()); - println!("abi_encoded_packed: {:?}", abi_encoded_packed.clone()); - assert_eq!(values.sol_name(), "(bytes32,address,uint256)"); - - let encoded: Bytes = abi_encoded.into(); - let hash_bytes = keccak256(encoded); - let hash = B256::from_slice(hash_bytes.as_slice()); - UserOperationHash(hash) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack.rs deleted file mode 100644 index f1b8e9ecb..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::sol_types::SolValue; - -pub mod hashed_call_data; -pub mod hashed_init_code; -pub mod hashed_paymaster_and_data; -pub mod max_priority_fee_per_gas_and_max_fee_per_gas; -pub mod verificaction_gas_limit_and_call_gas_limit; - -pub fn pack_user_operation(user_operation: &PackedUserOperation) -> Vec { - let hashed_init_code = - hashed_init_code::get_hashed_init_code(user_operation); - - let hashed_call_data = - hashed_call_data::get_hashed_call_data(user_operation); - - let hashed_paymaster_and_data = - hashed_paymaster_and_data::get_hashed_paymaster_and_data( - user_operation, - ); - - let verificaction_gas_limit_and_call_gas_limit_item = - verificaction_gas_limit_and_call_gas_limit::get_verificaction_gas_limit_and_call_gas_limit(user_operation); - - let max_priority_fee_per_gas_and_max_fee_per_gas_item = - max_priority_fee_per_gas_and_max_fee_per_gas::get_max_priority_fee_per_gas_and_max_fee_per_gas(user_operation); - - let items = ( - user_operation.sender, - user_operation.nonce, - hashed_init_code, - hashed_call_data, - verificaction_gas_limit_and_call_gas_limit_item, - user_operation.preVerificationGas, - max_priority_fee_per_gas_and_max_fee_per_gas_item, - hashed_paymaster_and_data, - ); - - items.abi_encode() -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_call_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_call_data.rs deleted file mode 100644 index fec9563a6..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_call_data.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{B256, keccak256}; - -pub fn get_hashed_call_data(user_operation: &PackedUserOperation) -> B256 { - keccak256(&user_operation.callData) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07; - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_hashed_call_data() { - let expected_hashed_call_data_hex = "0x0a8139e8d993db78f1d6b8682c7dcf9d4ef0b49b8bf883dc0a22a45b7aa7da2c"; - let user_operation = PackedUserOperationWrapperV07::mock().0; - let hashed_call_data = get_hashed_call_data(&user_operation); - assert_eq!(hashed_call_data.to_string(), expected_hashed_call_data_hex,); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_init_code.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_init_code.rs deleted file mode 100644 index a9d34c2ef..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_init_code.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{B256, keccak256}; - -pub fn get_hashed_init_code(user_operation: &PackedUserOperation) -> B256 { - keccak256(&user_operation.initCode) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07; - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_hashed_init_code() { - let expected_hashed_init_code_hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; - let user_operation = PackedUserOperationWrapperV07::mock().0; - let hashed_init_code = get_hashed_init_code(&user_operation); - assert_eq!(hashed_init_code.to_string(), expected_hashed_init_code_hex); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_paymaster_and_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_paymaster_and_data.rs deleted file mode 100644 index aef63352a..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/hashed_paymaster_and_data.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{B256, keccak256}; - -pub fn get_hashed_paymaster_and_data( - user_operation: &PackedUserOperation, -) -> B256 { - keccak256(&user_operation.paymasterAndData) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07; - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_hashed_paymaster_and_data() { - let expected_hashed_paymaster_and_data_hex = "0xfc0dffa735c71f138a00eaaafa56834aebf784e3e446612810f3f325cfb8eda9"; - let user_operation = PackedUserOperationWrapperV07::mock().0; - let hashed_paymaster_and_data = - get_hashed_paymaster_and_data(&user_operation); - assert_eq!( - hashed_paymaster_and_data.to_string(), - expected_hashed_paymaster_and_data_hex, - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs deleted file mode 100644 index cc67fdb30..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::B256; - -pub fn get_max_priority_fee_per_gas_and_max_fee_per_gas( - user_operation: &PackedUserOperation, -) -> B256 { - // gasFees already contains the packed fees - // It's 32 bytes: 16 bytes max_priority_fee_per_gas + 16 bytes max_fee_per_gas - let mut result = [0u8; 32]; - if user_operation.gasFees.len() >= 32 { - result.copy_from_slice(&user_operation.gasFees[0..32]); - } - B256::from(result) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07; - use alloy::primitives::fixed_bytes; - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_max_priority_fee_per_gas_and_max_fee_per_gas() { - let expected_max_priority_fee_per_gas_and_max_fee_per_gas = fixed_bytes!( - "00000000000000000000000043d4ca3500000000000000000000000417bbd4f1" - ); - let user_operation = PackedUserOperationWrapperV07::mock().0; - let max_priority_fee_per_gas_and_max_fee_per_gas = - get_max_priority_fee_per_gas_and_max_fee_per_gas(&user_operation); - assert_eq!( - max_priority_fee_per_gas_and_max_fee_per_gas, - expected_max_priority_fee_per_gas_and_max_fee_per_gas, - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/verificaction_gas_limit_and_call_gas_limit.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/verificaction_gas_limit_and_call_gas_limit.rs deleted file mode 100644 index c1a0ef1c3..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v07/pack/verificaction_gas_limit_and_call_gas_limit.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::B256; - -pub fn get_verificaction_gas_limit_and_call_gas_limit( - user_operation: &PackedUserOperation, -) -> B256 { - // accountGasLimits already contains the packed gas limits - // It's 32 bytes: 16 bytes verification_gas_limit + 16 bytes call_gas_limit - let mut result = [0u8; 32]; - if user_operation.accountGasLimits.len() >= 32 { - result.copy_from_slice(&user_operation.accountGasLimits[0..32]); - } - B256::from(result) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::wrapper_v07::PackedUserOperationWrapperV07; - use alloy::primitives::fixed_bytes; - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_verificaction_gas_limit_and_call_gas_limit() { - let expected_verification_gas_limit_and_call_gas_limit = fixed_bytes!( - "00000000000000000000000000010b2500000000000000000000000000013880" - ); - let user_operation = PackedUserOperationWrapperV07::mock().0; - let verification_gas_limit_and_call_gas_limit = - get_verificaction_gas_limit_and_call_gas_limit(&user_operation); - assert_eq!( - verification_gas_limit_and_call_gas_limit, - expected_verification_gas_limit_and_call_gas_limit, - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08.rs deleted file mode 100644 index 6e8caa6e1..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08.rs +++ /dev/null @@ -1,319 +0,0 @@ -use crate::erc4337::{ - entry_point::{EntryPoint, PackedUserOperation}, - user_operation::hash::{ - user_operation_hash::UserOperationHash, v08::pack::CodeReader, - }, -}; -use alloy::{ - primitives::{Address, B256}, - providers::Provider, - sol_types::Eip712Domain, -}; -use eyre::Result; - -pub mod pack; -pub mod user_operation_typed_data; - -pub struct PackedUserOperationWrapper(pub PackedUserOperation); - -impl PackedUserOperationWrapper { - pub fn mock() -> Self { - use alloy::primitives::{Address, B256, Bytes, U256}; - use std::str::FromStr; - - let sender = - Address::from_str("0x6bf1C0c174e11B933e7d8940aFADf8BB7B8d421C") - .unwrap(); - let nonce = U256::from(1); - let init_code = Bytes::from_str("0x").unwrap(); - let call_data = Bytes::from_str("0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000cb98643b8786950f0461f3b0edf99d88f274574d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000").unwrap(); - - // Pack accountGasLimits: verificationGasLimit (128 bits) + callGasLimit (128 bits) - let account_gas_limits = B256::from_str("0x00000000000000000000000000003fc30000000000000000000000000000e7a8").unwrap(); - - let pre_verification_gas = U256::from(52147); - - // Pack gasFees: maxPriorityFeePerGas (128 bits) + maxFeePerGas (128 bits) - let gas_fees = B256::from_str("0x0000000000000000000000008585115a00000000000000000000000077359400").unwrap(); - - let paymaster_and_data = Bytes::from_str("0x").unwrap(); - let signature = Bytes::from_str("0x00000000000000000000000000427edf0c3c3bd42188ab4c907759942abebd93000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); - - let user_operation = PackedUserOperation { - sender, - nonce, - initCode: init_code, - callData: call_data, - accountGasLimits: account_gas_limits, - preVerificationGas: pre_verification_gas, - gasFees: gas_fees, - paymasterAndData: paymaster_and_data, - signature, - }; - - Self(user_operation) - } -} - -pub async fn get_user_operation_hash_entry_point( - user_operation: &PackedUserOperation, - entry_point: &Address, - provider: P, -) -> eyre::Result { - let entry_point = EntryPoint::new(*entry_point, provider.clone()); - let hash = entry_point.getUserOpHash(user_operation.clone()).call().await?; - Ok(hash.into()) -} - -struct NoCodeReader; - -impl CodeReader for NoCodeReader { - fn get_code(&self, _address: &Address) -> Option> { - None - } -} - -pub fn get_user_operation_hash( - user_operation: &PackedUserOperation, - entry_point: &Address, - chain_id: u64, -) -> UserOperationHash { - get_user_operation_hash_with_code_reader( - user_operation, - entry_point, - chain_id, - &NoCodeReader, - ) -} - -pub async fn get_user_operation_hash_with_provider( - user_operation: &PackedUserOperation, - entry_point: &Address, - chain_id: u64, - provider: &P, -) -> Result { - let sender_code = - provider.get_code_at(user_operation.sender).latest().await?; - let reader = StaticCodeReader { code: Some(sender_code.to_vec()) }; - Ok(get_user_operation_hash_with_code_reader( - user_operation, - entry_point, - chain_id, - &reader, - )) -} - -struct StaticCodeReader { - code: Option>, -} -impl CodeReader for StaticCodeReader { - fn get_code(&self, _address: &Address) -> Option> { - self.code.clone() - } -} - -pub fn get_user_operation_hash_with_code_reader( - user_operation: &PackedUserOperation, - entry_point: &Address, - chain_id: u64, - code_reader: &dyn CodeReader, -) -> UserOperationHash { - // EIP-712 domain separator using typesafe macro - let domain: Eip712Domain = pack::build_domain(entry_point, chain_id); - - // EIP-712 struct hash for PackedUserOperation per v0.8 - let struct_hash = pack::struct_hash(user_operation, code_reader); - let digest = pack::eip712_digest(struct_hash, &domain); - - UserOperationHash(B256::from_slice(digest.as_slice())) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::{ - entry_point::PackedUserOperation, user_operation::hash::v08::pack::*, - }; - use alloy::primitives::{Address, Bytes, U256}; - use serde::{Deserialize, Serialize}; - use std::str::FromStr; - - #[derive(Deserialize, Serialize)] - #[serde(rename_all = "camelCase")] - struct TestData { - user_operation: UserOperationJson, - metadata: Metadata, - } - - #[derive(Deserialize, Serialize)] - #[serde(rename_all = "camelCase")] - struct UserOperationJson { - sender: String, - nonce: String, - init_code: String, - call_data: String, - call_gas_limit: String, - verification_gas_limit: String, - pre_verification_gas: String, - max_fee_per_gas: String, - max_priority_fee_per_gas: String, - paymaster_and_data: String, - signature: String, - } - - #[derive(Deserialize, Serialize)] - #[serde(rename_all = "camelCase")] - struct Metadata { - chain_id: u64, - entry_point_address: String, - entry_point_version: String, - } - - fn parse_hex_string(hex_str: &str) -> Bytes { - if hex_str.is_empty() || hex_str == "0x" { - Bytes::default() - } else { - Bytes::from_str(hex_str).unwrap() - } - } - - fn parse_u256_from_hex(hex_str: &str) -> U256 { - U256::from_str(hex_str).unwrap() - } - - fn parse_address_from_hex(hex_str: &str) -> Address { - Address::from_str(hex_str).unwrap() - } - - fn load_test_data() -> (PackedUserOperation, Address, u64) { - let json_path = "/Users/jackml/Developer/github/matter-labs/zksync-sso/packages/erc4337-contracts/test/integration/erc4337-userop.json"; - let json_content = std::fs::read_to_string(json_path) - .expect("Failed to read JSON file"); - let test_data: TestData = - serde_json::from_str(&json_content).expect("Failed to parse JSON"); - - // For EntryPoint PackedUserOperation, we need to construct the packed fields - let call_gas_limit = - parse_u256_from_hex(&test_data.user_operation.call_gas_limit); - let verification_gas_limit = parse_u256_from_hex( - &test_data.user_operation.verification_gas_limit, - ); - let max_fee_per_gas = - parse_u256_from_hex(&test_data.user_operation.max_fee_per_gas); - let max_priority_fee_per_gas = parse_u256_from_hex( - &test_data.user_operation.max_priority_fee_per_gas, - ); - - // Pack accountGasLimits: verificationGasLimit (128 bits) + callGasLimit (128 bits) - let account_gas_limits = { - let mut packed = [0u8; 32]; - let verification_bytes = verification_gas_limit.to_be_bytes::<32>(); - let call_bytes = call_gas_limit.to_be_bytes::<32>(); - packed[16..32].copy_from_slice(&verification_bytes[16..32]); // last 16 bytes - packed[0..16].copy_from_slice(&call_bytes[16..32]); // last 16 bytes - B256::from_slice(&packed) - }; - - // Pack gasFees: maxPriorityFeePerGas (128 bits) + maxFeePerGas (128 bits) - let gas_fees = { - let mut packed = [0u8; 32]; - let priority_bytes = max_priority_fee_per_gas.to_be_bytes::<32>(); - let fee_bytes = max_fee_per_gas.to_be_bytes::<32>(); - packed[16..32].copy_from_slice(&priority_bytes[16..32]); // last 16 bytes - packed[0..16].copy_from_slice(&fee_bytes[16..32]); // last 16 bytes - B256::from_slice(&packed) - }; - - let user_op = PackedUserOperation { - sender: parse_address_from_hex(&test_data.user_operation.sender), - nonce: parse_u256_from_hex(&test_data.user_operation.nonce), - initCode: parse_hex_string(&test_data.user_operation.init_code), - callData: parse_hex_string(&test_data.user_operation.call_data), - accountGasLimits: account_gas_limits, - preVerificationGas: parse_u256_from_hex( - &test_data.user_operation.pre_verification_gas, - ), - gasFees: gas_fees, - paymasterAndData: parse_hex_string( - &test_data.user_operation.paymaster_and_data, - ), - signature: parse_hex_string(&test_data.user_operation.signature), - }; - - let entry_point = - parse_address_from_hex(&test_data.metadata.entry_point_address); - let chain_id = test_data.metadata.chain_id; - - (user_op, entry_point, chain_id) - } - - struct MockCodeReader; - impl CodeReader for MockCodeReader { - fn get_code(&self, _address: &Address) -> Option> { - None - } - } - - #[test] - #[ignore = "need to investigate failure"] - fn test_get_user_operation_hash_with_code_reader() { - let (user_operation, entry_point, chain_id) = load_test_data(); - let code_reader = MockCodeReader; - - println!("=== User Operation Data ==="); - println!("sender: {}", user_operation.sender); - println!("nonce: {}", user_operation.nonce); - println!("initCode: {}", user_operation.initCode); - println!("callData: {}", user_operation.callData); - println!("accountGasLimits: {}", user_operation.accountGasLimits); - println!("preVerificationGas: {}", user_operation.preVerificationGas); - println!("gasFees: {}", user_operation.gasFees); - println!("paymasterAndData: {}", user_operation.paymasterAndData); - println!("signature: {}", user_operation.signature); - println!("entry_point: {}", entry_point); - println!("chain_id: {}", chain_id); - - let typehash = packed_userop_typehash(); - println!("\n=== Intermediary Values ==="); - println!("typehash: {}", typehash); - - let hash_init_code = hashed_init_code::get_hashed_init_code_with_7702( - &user_operation, - &code_reader, - ); - println!("hash_init_code: {}", hash_init_code); - - let hash_call_data = - hashed_call_data::get_hashed_call_data(&user_operation); - println!("hash_call_data: {}", hash_call_data); - - let account_gas_limits = verificaction_gas_limit_and_call_gas_limit::get_verificaction_gas_limit_and_call_gas_limit(&user_operation); - println!("account_gas_limits: {}", account_gas_limits); - - let gas_fees = max_priority_fee_per_gas_and_max_fee_per_gas::get_max_priority_fee_per_gas_and_max_fee_per_gas(&user_operation); - println!("gas_fees: {}", gas_fees); - - let hash_paymaster_and_data = - hashed_paymaster_and_data::get_hashed_paymaster_and_data( - &user_operation, - ); - println!("hash_paymaster_and_data: {}", hash_paymaster_and_data); - - let hash = get_user_operation_hash_with_code_reader( - &user_operation, - &entry_point, - chain_id, - &code_reader, - ); - let expected_hash = "0xc768eb19ae473522e7dd9cb928df330166c8edeaf7de5d419c91b92564a39765"; - let expected_user_op_hash = - UserOperationHash::from_str(expected_hash).unwrap(); - - println!("\n=== Final Results ==="); - println!("Computed hash: {}", hash.0); - println!("Expected hash: {}", expected_user_op_hash.0); - println!("Hashes match: {}", hash == expected_user_op_hash); - - assert_eq!(hash, expected_user_op_hash, "User operation hash mismatch"); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack.rs deleted file mode 100644 index 8fb30169c..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack.rs +++ /dev/null @@ -1,66 +0,0 @@ -pub mod domain; -pub mod hashed_call_data; -pub mod hashed_init_code; -pub mod hashed_paymaster_and_data; -pub mod max_priority_fee_per_gas_and_max_fee_per_gas; -pub mod verificaction_gas_limit_and_call_gas_limit; - -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::{ - primitives::{Address, B256, Bytes, U256, keccak256}, - sol_types::{Eip712Domain, SolValue}, -}; - -pub trait CodeReader { - fn get_code(&self, address: &Address) -> Option>; -} - -pub fn packed_userop_typehash() -> B256 { - keccak256(b"PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)") -} - -pub fn build_domain(entry_point: &Address, chain_id: u64) -> Eip712Domain { - domain::build_domain(entry_point, chain_id) -} - -pub fn struct_hash( - user_operation: &PackedUserOperation, - code_reader: &dyn CodeReader, -) -> B256 { - let typehash = packed_userop_typehash(); - let hash_init_code = hashed_init_code::get_hashed_init_code_with_7702( - user_operation, - code_reader, - ); - let hash_call_data = hashed_call_data::get_hashed_call_data(user_operation); - let account_gas_limits = verificaction_gas_limit_and_call_gas_limit::get_verificaction_gas_limit_and_call_gas_limit(user_operation); - let pre_verification_gas: U256 = user_operation.preVerificationGas; - let gas_fees = max_priority_fee_per_gas_and_max_fee_per_gas::get_max_priority_fee_per_gas_and_max_fee_per_gas(user_operation); - let hash_paymaster_and_data = - hashed_paymaster_and_data::get_hashed_paymaster_and_data( - user_operation, - ); - - let struct_tuple = ( - typehash, - user_operation.sender, - user_operation.nonce, - hash_init_code, - hash_call_data, - account_gas_limits, - pre_verification_gas, - gas_fees, - hash_paymaster_and_data, - ); - let struct_encoded: Bytes = struct_tuple.abi_encode().into(); - keccak256(struct_encoded) -} - -pub fn eip712_digest(struct_hash: B256, domain: &Eip712Domain) -> B256 { - // EIP-712 signing hash: keccak256("\x19\x01" || domainSeparator || structHash) - let mut data = Vec::new(); - data.extend_from_slice(b"\x19\x01"); - data.extend_from_slice(&domain.separator().0); - data.extend_from_slice(&struct_hash.0); - keccak256(data) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/domain.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/domain.rs deleted file mode 100644 index dc4d88d1e..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/domain.rs +++ /dev/null @@ -1,13 +0,0 @@ -use alloy::{ - primitives::Address, - sol_types::{Eip712Domain, eip712_domain}, -}; - -pub fn build_domain(entry_point: &Address, chain_id: u64) -> Eip712Domain { - eip712_domain! { - name: "ERC4337", - version: "1", - chain_id: chain_id, - verifying_contract: *entry_point, - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_call_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_call_data.rs deleted file mode 100644 index 0e3c552bf..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_call_data.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{B256, keccak256}; - -pub fn get_hashed_call_data(user_operation: &PackedUserOperation) -> B256 { - keccak256(&user_operation.callData) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::PackedUserOperationWrapper; - - #[test] - fn test_get_hashed_call_data() { - let expected_hashed_call_data_hex = "0x9b96c107f6dc8ddfee294703755263ee85a82c354d68eff28b0781ddcd2c9127"; - let user_operation = PackedUserOperationWrapper::mock().0; - let hashed_call_data = get_hashed_call_data(&user_operation); - assert_eq!(hashed_call_data.to_string(), expected_hashed_call_data_hex); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_init_code.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_init_code.rs deleted file mode 100644 index 7addaf1cb..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_init_code.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::v08::pack::CodeReader, -}; -use alloy::primitives::{Address, B256, keccak256}; - -pub fn get_hashed_init_code(user_operation: &PackedUserOperation) -> B256 { - keccak256(&user_operation.initCode) -} - -fn is_eip7702_init_code(init_code: &[u8]) -> bool { - if init_code.len() < 2 { - return false; - } - init_code[0] == 0x77 && init_code[1] == 0x02 -} - -fn eip7702_delegate_from_code(code: &[u8]) -> Option
{ - if code.len() < 23 { - return None; - } - if !(code[0] == 0xef && code[1] == 0x01 && code[2] == 0x00) { - return None; - } - Some(Address::from_slice(&code[3..23])) -} - -pub fn get_hashed_init_code_with_7702( - user_operation: &PackedUserOperation, - code_reader: &dyn CodeReader, -) -> B256 { - let init = &user_operation.initCode; - if !is_eip7702_init_code(init) { - return keccak256(init); - } - if let Some(code) = code_reader.get_code(&user_operation.sender) - && let Some(delegate) = eip7702_delegate_from_code(&code) - { - if init.len() <= 20 { - return keccak256(delegate.as_slice()); - } - let mut buf = Vec::with_capacity(20 + init.len().saturating_sub(20)); - buf.extend_from_slice(delegate.as_slice()); - buf.extend_from_slice(&init[20..]); - return keccak256(buf); - } - keccak256(init) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::PackedUserOperationWrapper; - use alloy::primitives::Address; - - struct MockCodeReader; - impl super::super::CodeReader for MockCodeReader { - fn get_code(&self, _address: &Address) -> Option> { - None - } - } - - #[test] - fn test_get_hashed_init_code() { - let expected_hashed_init_code_hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; - let user_operation = PackedUserOperationWrapper::mock().0; - let code_reader = MockCodeReader; - let hashed_init_code = - get_hashed_init_code_with_7702(&user_operation, &code_reader); - assert_eq!(hashed_init_code.to_string(), expected_hashed_init_code_hex); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_paymaster_and_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_paymaster_and_data.rs deleted file mode 100644 index b8e0fda8b..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/hashed_paymaster_and_data.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::primitives::{B256, Bytes, keccak256}; - -fn get_data(user_operation: &PackedUserOperation) -> Bytes { - // For EntryPoint PackedUserOperation, paymasterAndData is already the complete data - user_operation.paymasterAndData.clone() -} - -pub fn get_hashed_paymaster_and_data( - user_operation: &PackedUserOperation, -) -> B256 { - let data = get_data(user_operation); - keccak256(data) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::PackedUserOperationWrapper; - - #[test] - fn test_get_hashed_paymaster_and_data() { - let expected_hashed_paymaster_and_data_hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; - let user_operation = PackedUserOperationWrapper::mock().0; - let hashed_paymaster_and_data = - get_hashed_paymaster_and_data(&user_operation); - assert_eq!( - hashed_paymaster_and_data.to_string(), - expected_hashed_paymaster_and_data_hex - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs deleted file mode 100644 index 851c7ee16..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/max_priority_fee_per_gas_and_max_fee_per_gas.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::combine::combine_and_trim_first_16_bytes, -}; -use alloy::primitives::{B256, U256}; - -pub fn get_max_priority_fee_per_gas_and_max_fee_per_gas( - user_operation: &PackedUserOperation, -) -> B256 { - // For EntryPoint PackedUserOperation, we need to extract from gasFees - // gasFees is packed as: maxPriorityFeePerGas (128 bits) + maxFeePerGas (128 bits) - let gas_fees = user_operation.gasFees; - let max_priority_fee_per_gas = u128::from_be_bytes([ - gas_fees[16], - gas_fees[17], - gas_fees[18], - gas_fees[19], - gas_fees[20], - gas_fees[21], - gas_fees[22], - gas_fees[23], - gas_fees[24], - gas_fees[25], - gas_fees[26], - gas_fees[27], - gas_fees[28], - gas_fees[29], - gas_fees[30], - gas_fees[31], - ]); - let max_fee_per_gas = u128::from_be_bytes([ - gas_fees[0], - gas_fees[1], - gas_fees[2], - gas_fees[3], - gas_fees[4], - gas_fees[5], - gas_fees[6], - gas_fees[7], - gas_fees[8], - gas_fees[9], - gas_fees[10], - gas_fees[11], - gas_fees[12], - gas_fees[13], - gas_fees[14], - gas_fees[15], - ]); - - combine_and_trim_first_16_bytes( - U256::from(max_priority_fee_per_gas), - U256::from(max_fee_per_gas), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::PackedUserOperationWrapper; - - #[test] - fn test_get_max_priority_fee_per_gas_and_max_fee_per_gas() { - let expected_max_priority_fee_per_gas_and_max_fee_per_gas = "0x000000000000000000000000773594000000000000000000000000008585115a"; - let user_operation = PackedUserOperationWrapper::mock().0; - let max_priority_fee_per_gas_and_max_fee_per_gas = - get_max_priority_fee_per_gas_and_max_fee_per_gas(&user_operation); - assert_eq!( - max_priority_fee_per_gas_and_max_fee_per_gas.to_string(), - expected_max_priority_fee_per_gas_and_max_fee_per_gas - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/verificaction_gas_limit_and_call_gas_limit.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/verificaction_gas_limit_and_call_gas_limit.rs deleted file mode 100644 index ee73ed740..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/pack/verificaction_gas_limit_and_call_gas_limit.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::combine::combine_and_trim_first_16_bytes, -}; -use alloy::primitives::{B256, U256}; - -pub fn get_verificaction_gas_limit_and_call_gas_limit( - user_operation: &PackedUserOperation, -) -> B256 { - // For EntryPoint PackedUserOperation, we need to extract from accountGasLimits - // accountGasLimits is packed as: verificationGasLimit (128 bits) + callGasLimit (128 bits) - let account_gas_limits = user_operation.accountGasLimits; - let verification_gas_limit = u128::from_be_bytes([ - account_gas_limits[16], - account_gas_limits[17], - account_gas_limits[18], - account_gas_limits[19], - account_gas_limits[20], - account_gas_limits[21], - account_gas_limits[22], - account_gas_limits[23], - account_gas_limits[24], - account_gas_limits[25], - account_gas_limits[26], - account_gas_limits[27], - account_gas_limits[28], - account_gas_limits[29], - account_gas_limits[30], - account_gas_limits[31], - ]); - let call_gas_limit = u128::from_be_bytes([ - account_gas_limits[0], - account_gas_limits[1], - account_gas_limits[2], - account_gas_limits[3], - account_gas_limits[4], - account_gas_limits[5], - account_gas_limits[6], - account_gas_limits[7], - account_gas_limits[8], - account_gas_limits[9], - account_gas_limits[10], - account_gas_limits[11], - account_gas_limits[12], - account_gas_limits[13], - account_gas_limits[14], - account_gas_limits[15], - ]); - - combine_and_trim_first_16_bytes( - U256::from(verification_gas_limit), - U256::from(call_gas_limit), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::PackedUserOperationWrapper; - - #[test] - fn test_get_verificaction_gas_limit_and_call_gas_limit() { - let expected_verification_gas_limit_and_call_gas_limit = "0x0000000000000000000000000000e7a800000000000000000000000000003fc3"; - let user_operation = PackedUserOperationWrapper::mock().0; - let verification_gas_limit_and_call_gas_limit = - get_verificaction_gas_limit_and_call_gas_limit(&user_operation); - assert_eq!( - verification_gas_limit_and_call_gas_limit.to_string(), - expected_verification_gas_limit_and_call_gas_limit - ); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data.rs deleted file mode 100644 index b000ad018..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::erc4337::entry_point::PackedUserOperation; -use alloy::{ - primitives::{Address, FixedBytes}, - signers::{Signature, SignerSync, local::PrivateKeySigner}, - sol_types::{Eip712Domain, SolStruct, eip712_domain}, -}; - -pub mod constants; -pub mod hash_typed_data; -pub mod user_operation_hash; - -pub(crate) fn create_domain( - chain_id: u64, - entry_point_address: Address, -) -> Eip712Domain { - eip712_domain! { - name: "ERC4337", - version: "1", - chain_id: chain_id, - verifying_contract: entry_point_address, - } -} - -pub fn signing_hash( - chain_id: u64, - entry_point_address: Address, - user_operation: PackedUserOperation, -) -> eyre::Result> { - println!("Chain ID: {:?}", chain_id); - print!("Entry Point Address: {:?}", entry_point_address); - println!("User Operation: {:?}", user_operation); - let domain = create_domain(chain_id, entry_point_address); - println!("Domain: {:?}", domain); - let hash = user_operation.eip712_signing_hash(&domain); - println!("Hash: {:?}", hash); - Ok(hash) -} - -pub fn sign_user_operation( - chain_id: u64, - entry_point_address: Address, - user_operation: PackedUserOperation, - signer: PrivateKeySigner, -) -> eyre::Result { - let hash = signing_hash(chain_id, entry_point_address, user_operation)?; - let signature = signer.sign_hash_sync(&hash)?; - Ok(signature) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/constants.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/constants.rs deleted file mode 100644 index 1a3c26952..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/constants.rs +++ /dev/null @@ -1,50 +0,0 @@ -use alloy::{ - primitives::address, - sol, - sol_types::{Eip712Domain, SolStruct, eip712_domain}, -}; -use serde::Serialize; - -sol! { - #[allow(missing_docs)] - #[derive(Serialize)] - struct Person { - string name; - address wallet; - } - - #[allow(missing_docs)] - #[derive(Serialize)] - struct Mail { - Person from; - Person to; - string contents; - } -} - -pub struct HashPayload { - pub domain: Eip712Domain, - pub message: T, -} - -pub fn basic() -> HashPayload { - HashPayload { - domain: eip712_domain! { - name: "Ether Mail", - version: "1", - chain_id: 1, - verifying_contract: address!("0x0000000000000000000000000000000000000000"), - }, - message: Mail { - from: Person { - name: "Cow".to_string(), - wallet: address!("0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"), - }, - to: Person { - name: "Bob".to_string(), - wallet: address!("0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"), - }, - contents: "Hello, Bob!".to_string(), - }, - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/hash_typed_data.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/hash_typed_data.rs deleted file mode 100644 index b648d3d85..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/hash_typed_data.rs +++ /dev/null @@ -1,54 +0,0 @@ -use alloy::{ - primitives::{FixedBytes, hex}, - sol_types::{Eip712Domain, SolStruct}, -}; -use alloy_dyn_abi::TypedData; - -pub fn dyn_hash_typed_data( - typed_data: TypedData, -) -> eyre::Result> { - let hash = typed_data.eip712_signing_hash()?; - println!("Hash: {}", hex::encode(hash)); - Ok(hash) -} - -pub fn hash_typed_data( - ty: T, - domain: Eip712Domain, -) -> FixedBytes<32> { - let hash = ty.eip712_signing_hash(&domain); - println!("Hash: {}", hex::encode(hash)); - hash -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::hash::v08::user_operation_typed_data::constants; - - #[test] - fn test_hash_typed_data() -> eyre::Result<()> { - let payload = constants::basic(); - let message = payload.message; - let domain = payload.domain; - - let typed_data = TypedData::from_struct(&message, Some(domain.clone())); - - let hashed_type_data_dyn = dyn_hash_typed_data(typed_data)?; - - println!("Hashed Type Data Dyn: {}", hex::encode(hashed_type_data_dyn)); - - let hashed_typed_data = hash_typed_data(message, domain); - - println!("Hashed Typed Data: {}", hex::encode(hashed_typed_data)); - - eyre::ensure!( - hashed_type_data_dyn == hashed_typed_data, - "hashing functions should return identical data, dyn: {}, static: {}", - hex::encode(hashed_type_data_dyn), - hex::encode(hashed_typed_data) - ); - - Ok(()) - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/user_operation_hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/user_operation_hash.rs deleted file mode 100644 index 6563c60a8..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/hash/v08/user_operation_typed_data/user_operation_hash.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::v08::user_operation_typed_data::{ - create_domain, hash_typed_data::hash_typed_data, - }, -}; -use alloy::primitives::{Address, FixedBytes}; - -pub fn get_user_operation_hash( - chain_id: u64, - entry_point_address: Address, - user_operation: PackedUserOperation, -) -> FixedBytes<32> { - let domain = create_domain(chain_id, entry_point_address); - - hash_typed_data(user_operation, domain) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::erc4337::user_operation::UserOperationV08; - use alloy::primitives::{Bytes, U256, address, hex}; - - #[test] - #[ignore = "`get_user_operation_hash` is not returning the same hash as `viem` need to investigate"] - fn test_hash_typed_data() -> eyre::Result<()> { - let chain_id = 1; - let entry_point_address = - address!("0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); - - let user_op = UserOperationV08 { - call_data: Bytes::new(), - call_gas_limit: U256::from(6942069), - max_fee_per_gas: U256::from(69420), - max_priority_fee_per_gas: U256::from(69), - nonce: U256::from(0), - pre_verification_gas: U256::from(6942069), - sender: address!("0x1234567890123456789012345678901234567890"), - signature: Bytes::new(), - verification_gas_limit: U256::from(6942069), - ..Default::default() - }; - - let user_operation_packed = user_op.into(); - - let user_op_hash = get_user_operation_hash( - chain_id, - entry_point_address, - user_operation_packed, - ); - - println!("user_op_hash: {}", hex::encode(user_op_hash)); - - let expected_user_op_hash: FixedBytes<32> = hex::decode( - "0xa2224e732a1d4e2f923c7c05d586a0aa6cbc42172ec02f31d35fa9a2b8ba9208", - ) - .map(|bytes| FixedBytes::from_slice(&bytes))?; - - eyre::ensure!( - user_op_hash == expected_user_op_hash, - "user operation hash should match expected hash, received: {}, expected: {}", - hex::encode(user_op_hash), - hex::encode(expected_user_op_hash) - ); - - Ok(()) - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature.rs index ac51e75ec..8b1378917 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature.rs @@ -1 +1 @@ -pub mod v08; + diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08.rs deleted file mode 100644 index 68240e5b5..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::{ - hash::v08::get_user_operation_hash, - signature::v08::sign_hash::sign_user_operation_hash, - }, -}; -use alloy::{primitives::Address, signers::local::PrivateKeySigner}; - -pub mod sign_hash; - -pub fn sign_user_operation( - user_operation: &PackedUserOperation, - signer: &PrivateKeySigner, - chain_id: u64, - entry_point_address: Address, -) -> eyre::Result { - let hash = - get_user_operation_hash(user_operation, &entry_point_address, chain_id); - - let signature = sign_user_operation_hash(&hash, signer)?; - - let mut user_operation = user_operation.clone(); - user_operation.signature = signature; - - Ok(user_operation) -} - -#[cfg(test)] -mod tests { - use crate::test_utils::{ - TEST_EXPECTED_SIGNATURE, TEST_PRIVATE_KEY, load_userop_from_fixture, - }; - use alloy::{primitives::Bytes, signers::local::PrivateKeySigner}; - use std::str::FromStr; - - #[test] - fn test_sign_user_operation_matches_viem_example() { - let (user_operation, entry_point, chain_id) = load_userop_from_fixture( - "../../../../../erc4337-contracts/test/integration/erc4337-userop.json", - ); - - let signer = PrivateKeySigner::from_str(TEST_PRIVATE_KEY).unwrap(); - - let signed = super::sign_user_operation( - &user_operation, - &signer, - chain_id, - entry_point, - ) - .expect("failed to sign user operation"); - - let expected_signature = - Bytes::from_str(TEST_EXPECTED_SIGNATURE).unwrap(); - - assert_eq!(signed.signature, expected_signature); - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08/sign_hash.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08/sign_hash.rs deleted file mode 100644 index 72cb37941..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/signature/v08/sign_hash.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::erc4337::user_operation::hash::user_operation_hash::UserOperationHash; -use alloy::{ - primitives::{Bytes, FixedBytes}, - signers::{SignerSync, local::PrivateKeySigner}, -}; - -pub fn sign_user_operation_hash( - user_operation_hash: &UserOperationHash, - signer: &PrivateKeySigner, -) -> eyre::Result { - let hash_fixed: FixedBytes<32> = - FixedBytes::from_slice(user_operation_hash.as_fixed_bytes()); - let signature = signer.sign_hash_sync(&hash_fixed)?; - let signature_bytes = signature.as_bytes(); - let signature_fixed: FixedBytes<65> = - FixedBytes::from_slice(signature_bytes.as_slice()); - Ok(signature_fixed.into()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{ - TEST_EXPECTED_SIGNATURE, TEST_PRIVATE_KEY, TEST_USER_OP_HASH, - }; - use std::str::FromStr; - - #[test] - fn test_sign_user_operation_hash_matches_viem_example() -> eyre::Result<()> - { - let user_operation_hash = - UserOperationHash::from_str(TEST_USER_OP_HASH) - .expect("invalid hash"); - let signer = PrivateKeySigner::from_str(TEST_PRIVATE_KEY) - .map_err(|_| eyre::eyre!("invalid private key"))?; - - let signature = sign_user_operation_hash(&user_operation_hash, &signer) - .expect("failed to sign"); - - let expected = Bytes::from_str(TEST_EXPECTED_SIGNATURE) - .expect("invalid expected signature hex"); - - assert_eq!(signature, expected); - - Ok(()) - } -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests.rs deleted file mode 100644 index 247f856c9..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod basic_test; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests/basic_test.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests/basic_test.rs deleted file mode 100644 index a819ae010..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/tests/basic_test.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::wrapper_v07::PackedUserOperationWrapperV07, -}; -use alloy::{ - node_bindings::Anvil, - primitives::{Address, Bytes, FixedBytes, U256, address}, - providers::ProviderBuilder, -}; -use std::str::FromStr; - -impl PackedUserOperationWrapperV07 { - pub fn mock() -> Self { - let sender = address!("a3aBDC7f6334CD3EE466A115f30522377787c024"); - let nonce = U256::from(16); - let init_code = Bytes::from_str("").unwrap(); - let call_data = Bytes::from_str("b61d27f6000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa9604500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000").unwrap(); - - let max_fee_per_gas = U256::from(17578054897u64); - let max_priority_fee_per_gas = U256::from(1138018869u64); - - // Pack verification and call gas limits into 32 bytes - let verification_gas_limit = U256::from(68389); - let call_gas_limit = U256::from(80000); - let mut account_gas_limits_bytes = [0u8; 32]; - account_gas_limits_bytes[0..16] - .copy_from_slice(&verification_gas_limit.to_be_bytes::<16>()); - account_gas_limits_bytes[16..32] - .copy_from_slice(&call_gas_limit.to_be_bytes::<16>()); - let account_gas_limits = - FixedBytes::<32>::from(account_gas_limits_bytes); - - // Pack priority and max fee into 32 bytes - let mut gas_fees_bytes = [0u8; 32]; - gas_fees_bytes[0..16] - .copy_from_slice(&max_priority_fee_per_gas.to_be_bytes::<16>()); - gas_fees_bytes[16..32] - .copy_from_slice(&max_fee_per_gas.to_be_bytes::<16>()); - let gas_fees = FixedBytes::<32>::from(gas_fees_bytes); - - let signature = Bytes::from_str("a15569dd8f8324dbeabf8073fdec36d4b754f53ce5901e283c6de79af177dc94557fa3c9922cd7af2a96ca94402d35c39f266925ee6407aeb32b31d76978d4ba1c").unwrap(); - let pre_verification_gas = U256::from(55721); - - // Combine paymaster data - let paymaster_and_data = Bytes::from_str("0000000000000039cd5e8aE05257CE51C473ddd100000066cc6b8b000000000000bce787423a07dde9c43cdf50ff33bf35b18babd336cc9739fd9f6dca86e200934505c311454b60c3aa1d206e6bb893f3489e77ace4c58f30d47cebd368a1422a1c").unwrap(); - - Self(PackedUserOperation { - sender, - nonce, - initCode: init_code, - callData: call_data, - accountGasLimits: account_gas_limits, - preVerificationGas: pre_verification_gas, - gasFees: gas_fees, - paymasterAndData: paymaster_and_data, - signature, - }) - } -} - -#[tokio::test] -async fn test_send_user_operation() -> eyre::Result<()> { - let anvil = Anvil::new(); - let anvil_instance = anvil.spawn(); - let anvil_url = anvil_instance.endpoint_url(); - let wallet = anvil_instance.wallet().unwrap(); - - let _provider = - ProviderBuilder::new().wallet(wallet).connect_http(anvil_url); - - // TODO - // 1. deploy contracts - // 2. run alto - - let _receipient = Address::default(); - - // let execution_lib_instance = { - // use crate::erc4337::user_operation::v08::contracts::ExecutionLib; - // let execution_lib_address = Address::default(); // TODO - // ExecutionLib::new(execution_lib_address, provider) - // }; - // let execution = execution_lib_instance.encode_single - - // use crate::erc4337::account::erc7579::account::IERC7579Account; - - // function test_transfer() public { - // address recipient = makeAddr("recipient"); - // bytes memory execution = ExecutionLib.encodeSingle(recipient, 1 ether, ""); - // bytes memory callData = abi.encodeCall(IERC7579Account.execute, (ModeLib.encodeSimpleSingle(), execution)); - // PackedUserOperation[] memory userOps = new PackedUserOperation[](1); - // userOps[0] = makeSignedUserOp(callData, owner.key, address(eoaValidator)); - - // entryPoint.handleOps(userOps, bundler); - // vm.assertEq(recipient.balance, 1 ether, "Value not transferred via simple call"); - // } - - Ok(()) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/wrapper_v07.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/wrapper_v07.rs deleted file mode 100644 index 158d04871..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/user_operation/wrapper_v07.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::erc4337::{ - entry_point::PackedUserOperation, - user_operation::hash::{user_operation_hash::UserOperationHash, v07}, -}; -use alloy::primitives::{Address, Bytes, FixedBytes, U256}; - -pub struct PackedUserOperationWrapperV07(pub PackedUserOperation); - -impl Default for PackedUserOperationWrapperV07 { - fn default() -> Self { - let inner = PackedUserOperation { - sender: Address::ZERO, - nonce: U256::ZERO, - initCode: Bytes::default(), - callData: Bytes::default(), - accountGasLimits: FixedBytes::<32>::ZERO, // 16 bytes verification + 16 bytes call - preVerificationGas: U256::ZERO, - gasFees: FixedBytes::<32>::ZERO, // 16 bytes priority + 16 bytes max fee - paymasterAndData: Bytes::default(), - signature: Bytes::default(), - }; - Self(inner) - } -} - -impl From for PackedUserOperationWrapperV07 { - fn from(value: PackedUserOperation) -> Self { - Self(value) - } -} - -impl PackedUserOperationWrapperV07 { - /// Calculates the hash of the user operation - pub fn hash( - &self, - entry_point: &Address, - chain_id: u64, - ) -> UserOperationHash { - v07::get_user_operation_hash(&self.0, entry_point, chain_id) - } -} - -pub fn create_user_operation() -> PackedUserOperation { - PackedUserOperationWrapperV07::default().0 -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils.rs index 6e8937a10..8ee2440a6 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils.rs @@ -1,2 +1,5 @@ pub mod check_deployed; pub mod deployment_utils; + +#[cfg(any(test, feature = "test-utilities"))] +pub mod alto_test_utils; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/client/alto_test_utils.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/alto_test_utils.rs similarity index 100% rename from packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/client/alto_test_utils.rs rename to packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/alto_test_utils.rs diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/deployment_utils.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/deployment_utils.rs index b0938115d..7212b3ffd 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/deployment_utils.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/erc4337/utils/deployment_utils.rs @@ -119,6 +119,8 @@ pub async fn deploy_contracts( extract_contract_address(&lines, "SessionKeyValidator")?; let webauthn_validator = extract_contract_address(&lines, "WebAuthnValidator")?; + let guardian_executor = + extract_contract_address(&lines, "GuardianExecutor")?; let account_factory = extract_contract_address(&lines, "MSAFactory")?; let contracts = Contracts { @@ -127,6 +129,7 @@ pub async fn deploy_contracts( webauthn_validator, account_factory, entry_point, + guardian_executor, }; let provider = { diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/test_utils.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/test_utils.rs index 2d0b6b3fd..0be3e49b4 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/test_utils.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/test_utils.rs @@ -1,4 +1,4 @@ -use crate::erc4337::entry_point::PackedUserOperation; +use crate::erc4337::entry_point::contract::PackedUserOperation; use alloy::primitives::{Address, B256, Bytes, U256}; use serde::{Deserialize, Serialize}; use std::str::FromStr; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/utils/alloy_utilities/test_utilities.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/utils/alloy_utilities/test_utilities.rs index 7b2891ead..3f671a50e 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/utils/alloy_utilities/test_utilities.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/src/utils/alloy_utilities/test_utilities.rs @@ -2,8 +2,10 @@ use crate::{ config::contracts::Contracts, erc4337::{ bundler::pimlico::client::BundlerClient, - client::alto_test_utils::{AltoTestHelper, AltoTestHelperConfig}, - utils::deployment_utils::deploy_contracts_default, + utils::{ + alto_test_utils::{AltoTestHelper, AltoTestHelperConfig}, + deployment_utils::deploy_contracts_default, + }, }, utils::alloy_utilities::ethereum_wallet_from_private_key, }; diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/tests/integration.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/tests/integration.rs deleted file mode 100644 index 719b9bf7d..000000000 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-core/tests/integration.rs +++ /dev/null @@ -1,71 +0,0 @@ -use alloy::{ - primitives::Address, providers::ProviderBuilder, - signers::local::PrivateKeySigner, -}; -use std::str::FromStr; -use url::Url; -use zksync_sso_erc4337_core::{ - chain::{Chain, id::ChainId}, - config::{Config, contracts::Contracts}, - erc4337::{ - client::Client, entry_point::version::EntryPointVersion, - user_operation::UserOperationV08, - }, -}; - -#[tokio::test] -async fn test_integration() -> eyre::Result<()> { - // Test private key (from the JavaScript test) - let private_key_hex = - "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6"; - - // Create signer from private key - let signer = PrivateKeySigner::from_str(private_key_hex)?; - - // Set up contract addresses (these would typically come from your deployment) - let contracts = Contracts::new( - Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789")?, // EntryPoint v0.7 - Address::from_str("0x9406Cc6185a346906296840746125a0E44976454")?, // AccountFactory placeholder - Address::from_str("0x9406Cc6185a346906296840746125a0E44976454")?, // EOAValidator placeholder - Address::from_str("0x9406Cc6185a346906296840746125a0E44976454")?, // WebAuthnValidator placeholder, - Address::from_str("0x9406Cc6185a346906296840746125a0E44976454")?, // placeholder - ); - - // Anvil - let rpc_url: Url = "http://localhost:8545".parse()?; - - let chain = Chain::new( - ChainId::ETHEREUM_MAINNET, - EntryPointVersion::V08, - "Mainnet".to_string(), - ); - - // Configure the client - let config = Config::new( - rpc_url.clone(), // Anvil RPC - "http://localhost:4337".parse()?, // Alto bundler (typical port) - chain, - contracts, - ); - - // Create provider using the simpler HTTP transport - let provider = ProviderBuilder::new().connect_http(rpc_url); - - // Initialize the client - let _client = Client::new(config, signer, provider); - - // Create a simple user operation (currently just testing initialization) - let user_operation = UserOperationV08::default(); - - // For now, just test that we can create the client and user operation - // Full integration test would require: - // 1. Running anvil locally - // 2. Deploying contracts - // 3. Running Alto bundler - // 4. Sending actual user operations - - println!("Client initialized successfully"); - println!("User operation created: {:?}", user_operation); - - Ok(()) -} diff --git a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-ffi-web/src/lib.rs b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-ffi-web/src/lib.rs index f3923e83f..f69ae6651 100644 --- a/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-ffi-web/src/lib.rs +++ b/packages/sdk-platforms/rust/zksync-sso-erc4337/crates/zksync-sso-erc4337-ffi-web/src/lib.rs @@ -1,10 +1,11 @@ use alloy::{ network::EthereumWallet, - primitives::{Address, Bytes, FixedBytes, U256, keccak256}, + primitives::{Address, B256, Bytes, FixedBytes, U256, Uint, keccak256}, providers::ProviderBuilder, rpc::types::erc4337::PackedUserOperation as AlloyPackedUserOperation, signers::local::PrivateKeySigner, - sol_types::SolCall, + sol, + sol_types::SolValue, }; use alloy_rpc_client::RpcClient; use wasm_bindgen::prelude::*; @@ -13,17 +14,57 @@ use zksync_sso_erc4337_core::{ chain::{Chain, id::ChainId}, config::contracts::Contracts as CoreContracts, erc4337::{ - account::modular_smart_account::{ - add_passkey::PasskeyPayload as CorePasskeyPayload, - deploy::{ - EOASigners as CoreEOASigners, - WebAuthNSigner as CoreWebauthNSigner, + account::{ + erc7579::calls::encoded_call_data as encoded_call_data_core, + modular_smart_account::{ + deploy::{ + DeployAccountParams as DeployAccountParamsCore, + EOASigners as CoreEOASigners, + WebAuthNSigner as CoreWebauthNSigner, + deploy_account as deploy_account_core, + deploy_accout_call_data, + initialize_account_call_data as initialize_account_call_data_core, + }, + passkey::{ + add::{ + AddPasskeyParams as AddPasskeyParamsCore, + PasskeyPayload as CorePasskeyPayload, + add_passkey as add_passkey_core, + add_passkey_call_data as add_passkey_call_data_core, + }, + list::get_account_list_call_data as get_account_list_call_data_core, + }, + send::eoa::EOASendParams, + session::{ + create::create_session_call_data as create_session_call_data_core, + session_lib::session_spec::SessionSpec, + state::session_state_call_data as session_state_call_data_core, + }, + signers::{ + eoa::{ + eoa_sign as eoa_sign_core, + eoa_signature as eoa_signature_core, + stub_signature_eoa as stub_signature_eoa_core, + }, + passkey::stub_signature_passkey as stub_signature_passkey_core, + }, }, - send::eoa::EOASendParams, - signers::passkey::stub_signature_passkey, }, - entry_point::version::EntryPointVersion, + bundler::{ + Bundler, config::BundlerConfig as BundlerConfigCore, + pimlico::client::BundlerClient as BundlerClientCore, + }, + entry_point::{ + PackedUserOperation, + nonce::{ + GetNonceWithKeyParams, get_nonce_call_data, + get_nonce_with_key as get_nonce_with_key_core, + }, + user_op_hash::get_user_op_hash_call_data, + version::EntryPointVersion, + }, signer::create_eoa_signer, + user_operation::hash::user_operation_hash::get_user_operation_hash_entry_point as get_user_operation_hash_entry_point_core, }, }; @@ -500,16 +541,14 @@ pub fn deploy_account( console_log!(" Calling core deploy_account..."); // Use the core crate's deploy_account function - match zksync_sso_erc4337_core::erc4337::account::modular_smart_account::deploy::deploy_account( - zksync_sso_erc4337_core::erc4337::account::modular_smart_account::deploy::DeployAccountParams { - factory_address: factory_addr, - eoa_signers, - webauthn_signer, - session_validator: None, - id: None, - provider, - } - ) + match deploy_account_core(DeployAccountParamsCore { + factory_address: factory_addr, + eoa_signers, + webauthn_signer, + session_validator: None, + id: None, + provider, + }) .await { Ok(address) => { @@ -622,8 +661,7 @@ pub fn add_passkey_to_account( // Create bundler client let bundler_client = { - use zksync_sso_erc4337_core::erc4337::bundler::config::BundlerConfig; - let config = BundlerConfig::new(config.bundler_url); + let config = BundlerConfigCore::new(config.bundler_url); zksync_sso_erc4337_core::erc4337::bundler::pimlico::client::BundlerClient::new(config) }; @@ -654,15 +692,16 @@ pub fn add_passkey_to_account( console_log!(" Calling add_passkey..."); // Call the core add_passkey function - match zksync_sso_erc4337_core::erc4337::account::modular_smart_account::add_passkey::add_passkey( - account, - core_passkey, + match add_passkey_core(AddPasskeyParamsCore { + account_address: account, + passkey: core_passkey, webauthn_validator, - entry_point, + entry_point_address: entry_point, + paymaster: None, provider, bundler_client, signer, - ) + }) .await { Ok(_) => { @@ -671,10 +710,7 @@ pub fn add_passkey_to_account( } Err(e) => { console_log!(" Error adding passkey: {}", e); - Err(JsValue::from_str(&format!( - "Failed to add passkey: {}", - e - ))) + Err(JsValue::from_str(&format!("Failed to add passkey: {}", e))) } } }) @@ -813,23 +849,15 @@ pub fn send_transaction_eoa( // Create bundler client let bundler_client = { - use zksync_sso_erc4337_core::erc4337::bundler::config::BundlerConfig; - let config = BundlerConfig::new(config.bundler_url); - zksync_sso_erc4337_core::erc4337::bundler::pimlico::client::BundlerClient::new(config) + let config = BundlerConfigCore::new(config.bundler_url); + BundlerClientCore::new(config) }; console_log!(" Created bundler client"); // Encode the execution call - use zksync_sso_erc4337_core::erc4337::account::erc7579::{ - Execution, calls::encode_calls, - }; - - let call = - Execution { target: to, value: value_u256, data: data_bytes }; - - let calls = vec![call]; - let encoded_calls: Bytes = encode_calls(calls).into(); + let encoded_calls: Bytes = + encoded_call_data_core(to, Some(data_bytes), Some(value_u256)); console_log!( " Encoded call data, calling core send_transaction_eoa..." @@ -965,32 +993,30 @@ pub fn prepare_passkey_user_operation( console_log!(" Created provider and transport"); // Encode the execution call - use zksync_sso_erc4337_core::erc4337::account::erc7579::{ - Execution, calls::encode_calls, - }; - - let call = - Execution { target: to, value: value_u256, data: data_bytes }; - let calls = vec![call]; - let encoded_calls: Bytes = encode_calls(calls).into(); + let encoded_calls: Bytes = + encoded_call_data_core(to, Some(data_bytes), Some(value_u256)); console_log!(" Encoded call data"); // Build UserOperation with fixed high gas values (no bundler estimation) - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::nonce::get_nonce; - use alloy::primitives::Uint; let nonce_key = Uint::from(0); - let nonce = - match get_nonce(entry_point, account, nonce_key, &provider).await { - Ok(n) => n, - Err(e) => { - return Err(JsValue::from_str(&format!( - "Failed to get nonce: {}", - e - ))); - } - }; + let nonce = match get_nonce_with_key_core(GetNonceWithKeyParams { + sender: account, + entry_point, + key: nonce_key, + provider: provider.clone(), + }) + .await + { + Ok(n) => n, + Err(e) => { + return Err(JsValue::from_str(&format!( + "Failed to get nonce: {}", + e + ))); + } + }; // Parse validator address to create stub signature let validator = match webauthn_validator_address.parse::
() { @@ -1005,7 +1031,7 @@ pub fn prepare_passkey_user_operation( // Create stub signature internally (validator address + minimal ABI-encoded data) // This matches the format expected by WebAuthnValidator: (bytes authenticatorData, string clientDataJSON, bytes32[2] rs, bytes credentialId) - let stub_sig = match stub_signature_passkey(validator) { + let stub_sig = match stub_signature_passkey_core(validator) { Ok(sig) => sig, Err(e) => { return Err(JsValue::from_str(&format!( @@ -1057,7 +1083,6 @@ pub fn prepare_passkey_user_operation( (user_op.max_priority_fee_per_gas << 128) | user_op.max_fee_per_gas; // Create PackedUserOperation for hashing (EntryPoint format with packed fields) - use zksync_sso_erc4337_core::erc4337::entry_point::EntryPoint::PackedUserOperation; let packed_user_op = PackedUserOperation { sender: user_op.sender, nonce: user_op.nonce, @@ -1071,9 +1096,7 @@ pub fn prepare_passkey_user_operation( }; // Get the hash that needs to be signed - use zksync_sso_erc4337_core::erc4337::user_operation::hash::v08::get_user_operation_hash_entry_point; - - let hash = match get_user_operation_hash_entry_point( + let hash = match get_user_operation_hash_entry_point_core( &packed_user_op, &entry_point, provider.clone(), @@ -1092,7 +1115,7 @@ pub fn prepare_passkey_user_operation( console_log!(" UserOperation hash: {:?}", hash); // Convert hash to B256 for hex format - let hash_b256: alloy::primitives::B256 = hash.into(); + let hash_b256: B256 = hash.into(); let hash_hex = format!("{:#x}", hash_b256); // Create PreparedUserOperation struct to return to JavaScript (stateless design) @@ -1337,9 +1360,8 @@ pub fn submit_passkey_user_operation( // Create bundler client let bundler_client = { - use zksync_sso_erc4337_core::erc4337::bundler::config::BundlerConfig; - let config = BundlerConfig::new(config.bundler_url); - zksync_sso_erc4337_core::erc4337::bundler::pimlico::client::BundlerClient::new(config) + let config = BundlerConfigCore::new(config.bundler_url); + BundlerClientCore::new(config) }; // Parse entry point address for bundler call @@ -1354,7 +1376,6 @@ pub fn submit_passkey_user_operation( }; // Submit UserOperation - use zksync_sso_erc4337_core::erc4337::bundler::Bundler; match bundler_client.send_user_operation(entry_point, user_op).await { Ok(user_op_hash) => { @@ -1402,6 +1423,7 @@ pub fn parse_contract_addresses( webauthn_validator: &str, eoa_validator: &str, session_validator: &str, + guardian_executor: &str, ) -> Result { match CoreContracts::from_string( entry_point.to_string(), @@ -1409,6 +1431,7 @@ pub fn parse_contract_addresses( webauthn_validator.to_string(), eoa_validator.to_string(), session_validator.to_string(), + guardian_executor.to_string(), ) { Ok(contracts) => Ok(format!( "Entry Point: {:?}, Account Factory: {:?}, WebAuthn Validator: {:?}, EOA Validator: {:?}", @@ -1448,9 +1471,6 @@ pub fn encode_get_nonce_call_data( sender: String, key: String, ) -> Result { - use alloy::sol_types::SolCall; - use zksync_sso_erc4337_core::erc4337::entry_point::EntryPoint; - // Parse sender address let sender_addr = sender.parse::
().map_err(|e| { JsValue::from_str(&format!("Invalid sender address: {}", e)) @@ -1461,11 +1481,8 @@ pub fn encode_get_nonce_call_data( .parse::>() .unwrap_or(alloy::primitives::Uint::from(0)); - // Create the getNonce call - let call = EntryPoint::getNonceCall { sender: sender_addr, key: key_u192 }; - // Encode the call - let encoded = call.abi_encode(); + let encoded = get_nonce_call_data(sender_addr, key_u192); Ok(format!("0x{}", hex::encode(encoded))) } @@ -1493,23 +1510,13 @@ pub fn encode_get_account_list_call_data( domain: String, credential_id: String, ) -> Result { - use alloy::sol_types::SolCall; - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::WebAuthnValidator; - // Parse credential_id (can be 0x-prefixed or not) let cred_id_hex = credential_id.trim_start_matches("0x"); let cred_id_bytes = hex::decode(cred_id_hex).map_err(|e| { JsValue::from_str(&format!("Invalid credential_id hex: {}", e)) })?; - // Create the getAccountList call - let call = WebAuthnValidator::getAccountListCall { - domain, - credentialId: Bytes::from(cred_id_bytes), - }; - - // Encode the call - let encoded = call.abi_encode(); + let encoded = get_account_list_call_data_core(domain, cred_id_bytes); Ok(format!("0x{}", hex::encode(encoded))) } @@ -1553,10 +1560,6 @@ pub fn encode_execute_call_data( value: String, data: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::erc7579::{ - Execution, calls::encode_calls, - }; - // Parse target address let target_addr = target.parse::
().map_err(|e| { JsValue::from_str(&format!("Invalid target address: {}", e)) @@ -1578,12 +1581,9 @@ pub fn encode_execute_call_data( Bytes::from(bytes_vec) }; - // Create execution - let execution = - Execution { target: target_addr, value: value_u256, data: data_bytes }; - // Encode single call - let encoded = encode_calls(vec![execution]); + let encoded = + encoded_call_data_core(target_addr, Some(data_bytes), Some(value_u256)); Ok(format!("0x{}", hex::encode(encoded))) } @@ -1594,15 +1594,13 @@ pub fn encode_execute_call_data( pub fn generate_eoa_stub_signature( validator_address: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::signers::eoa::stub_signature_eoa; - // Parse validator address let validator_addr = validator_address.parse::
().map_err(|e| { JsValue::from_str(&format!("Invalid validator address: {}", e)) })?; // Generate stub signature - let signature = stub_signature_eoa(validator_addr).map_err(|e| { + let signature = stub_signature_eoa_core(validator_addr).map_err(|e| { JsValue::from_str(&format!("Failed to generate stub signature: {}", e)) })?; @@ -1688,9 +1686,6 @@ pub fn generate_session_stub_signature_wasm( pub fn encode_create_session_call_data( session_spec_json: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::session::session_lib::session_spec::SessionSpec; - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::session::SessionKeyValidator; - // Parse SessionSpec from JSON let spec: SessionSpec = serde_json::from_str(&session_spec_json).map_err(|e| { @@ -1698,9 +1693,7 @@ pub fn encode_create_session_call_data( })?; // Build createSession call and ABI encode - let create_session_calldata = - SessionKeyValidator::createSessionCall { sessionSpec: spec.into() } - .abi_encode(); + let create_session_calldata = create_session_call_data_core(spec); Ok(format!("0x{}", hex::encode(create_session_calldata))) } @@ -1712,10 +1705,6 @@ pub fn encode_session_state_call_data( account: String, session_spec_json: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::session::session_lib::session_spec::SessionSpec; - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::session::SessionKeyValidator; - use alloy::primitives::Address; - // Parse account address let account_addr = account.parse::
().map_err(|e| { JsValue::from_str(&format!("Invalid account address: {}", e)) @@ -1728,11 +1717,7 @@ pub fn encode_session_state_call_data( })?; // Build sessionState call and ABI encode - let call_data = SessionKeyValidator::sessionStateCall { - account: account_addr, - spec: spec.into(), - } - .abi_encode(); + let call_data = session_state_call_data_core(account_addr, spec); Ok(format!("0x{}", hex::encode(call_data))) } @@ -1815,15 +1800,13 @@ pub fn sign_eoa_message( private_key: String, message_hash: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::signers::eoa::eoa_sign; - // Parse message hash let hash = message_hash.parse::>().map_err(|e| { JsValue::from_str(&format!("Invalid message hash: {}", e)) })?; // Sign the hash - let signature = eoa_sign(&private_key, hash).map_err(|e| { + let signature = eoa_sign_core(&private_key, hash).map_err(|e| { JsValue::from_str(&format!("Failed to sign message: {}", e)) })?; @@ -1839,8 +1822,6 @@ pub fn sign_eoa_user_operation_hash( private_key: String, validator_address: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::signers::eoa::eoa_signature; - // Parse user operation hash let hash = user_op_hash.parse::>().map_err(|e| { JsValue::from_str(&format!("Invalid user operation hash: {}", e)) @@ -1852,8 +1833,8 @@ pub fn sign_eoa_user_operation_hash( })?; // Sign the hash with validator address prefix (85 bytes total: 20 + 65) - let signature = - eoa_signature(&private_key, validator_addr, hash).map_err(|e| { + let signature = eoa_signature_core(&private_key, validator_addr, hash) + .map_err(|e| { JsValue::from_str(&format!("Failed to sign user operation: {}", e)) })?; @@ -1961,8 +1942,6 @@ pub fn encode_get_user_operation_hash_call_data( max_fee_per_gas, max_priority_fee_per_gas, } = params; - use alloy::sol_types::SolCall; - use zksync_sso_erc4337_core::erc4337::entry_point::EntryPoint; // Parse addresses let sender_addr = sender.parse::
().map_err(|e| { @@ -2015,7 +1994,6 @@ pub fn encode_get_user_operation_hash_call_data( (max_priority_fee_per_gas_u256 << 128) | max_fee_per_gas_u256; // Create PackedUserOperation - use zksync_sso_erc4337_core::erc4337::entry_point::EntryPoint::PackedUserOperation; let packed_user_op = PackedUserOperation { sender: sender_addr, nonce: nonce_u256, @@ -2028,11 +2006,14 @@ pub fn encode_get_user_operation_hash_call_data( signature: Bytes::default(), // Empty signature for hash computation }; - // Create the getUserOpHash call - let call = EntryPoint::getUserOpHashCall { userOp: packed_user_op }; - // Encode the call data - let encoded = call.abi_encode(); + let encoded = get_user_op_hash_call_data(packed_user_op).map_err(|e| { + JsValue::from_str(&format!( + "Failed to get user operation hash call data: {}", + e + )) + })?; + Ok(format!("0x{}", hex::encode(encoded))) } @@ -2042,17 +2023,19 @@ pub fn encode_get_user_operation_hash_call_data( pub fn generate_passkey_stub_signature( validator_address: String, ) -> Result { - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::signers::passkey::stub_signature_passkey; - // Parse validator address let validator_addr = validator_address.parse::
().map_err(|e| { JsValue::from_str(&format!("Invalid validator address: {}", e)) })?; // Generate stub signature - let signature = stub_signature_passkey(validator_addr).map_err(|e| { - JsValue::from_str(&format!("Failed to generate stub signature: {}", e)) - })?; + let signature = + stub_signature_passkey_core(validator_addr).map_err(|e| { + JsValue::from_str(&format!( + "Failed to generate stub signature: {}", + e + )) + })?; Ok(format!("0x{}", hex::encode(&signature))) } @@ -2163,13 +2146,11 @@ pub fn encode_deploy_account_call_data( webauthn_validator_address: Option, session_validator_address: Option, ) -> Result { - use alloy::sol_types::SolCall; use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::{ - MSAFactory, - add_passkey::PasskeyPayload as CorePasskeyPayload, deploy::{ EOASigners as CoreEOASigners, WebAuthNSigner as CoreWebauthNSigner, }, + passkey::add::PasskeyPayload as CorePasskeyPayload, }; // Parse account ID @@ -2285,14 +2266,8 @@ pub fn encode_deploy_account_call_data( session_validator_core, ); - // Create the deployAccount call - let call = MSAFactory::deployAccountCall { - salt: account_id_fixed, - initData: init_data, - }; - // Encode the call - let encoded = call.abi_encode(); + let encoded = deploy_accout_call_data(account_id_fixed, init_data); Ok(format!("0x{}", hex::encode(encoded))) } @@ -2310,15 +2285,6 @@ pub fn encode_add_passkey_call_data( passkey_payload: PasskeyPayload, webauthn_validator_address: String, ) -> Result { - use alloy::{primitives::U256, sol_types::SolCall}; - use zksync_sso_erc4337_core::erc4337::account::{ - erc7579::{Execution, calls::encode_calls}, - modular_smart_account::{ - WebAuthnValidator, - add_passkey::PasskeyPayload as CorePasskeyPayload, - }, - }; - // Parse webauthn validator address let webauthn_validator = webauthn_validator_address.parse::
().map_err(|e| { @@ -2352,25 +2318,8 @@ pub fn encode_add_passkey_call_data( origin_domain: passkey_payload.origin_domain, }; - // Create addValidationKey call data - let add_validation_key_calldata = WebAuthnValidator::addValidationKeyCall { - credentialId: core_passkey.credential_id, - newKey: core_passkey.passkey, - domain: core_passkey.origin_domain, - } - .abi_encode() - .into(); - - // Wrap in Execution struct - let call = Execution { - target: webauthn_validator, - value: U256::from(0), - data: add_validation_key_calldata, - }; - // Encode as execute call - let calls = vec![call]; - let encoded = encode_calls(calls); + let encoded = add_passkey_call_data_core(core_passkey, webauthn_validator); Ok(format!("0x{}", hex::encode(encoded))) } @@ -2378,16 +2327,10 @@ pub fn encode_add_passkey_call_data( /// Helper function to create init data for deployment /// Mirrors the logic from deploy.rs create_init_data and modules_from_signers fn create_init_data_for_deployment( - eoa_signers: Option, - webauthn_signer: Option, + eoa_signers: Option, + webauthn_signer: Option, session_validator: Option
, ) -> Bytes { - use alloy::{ - sol, - sol_types::{SolCall, SolValue}, - }; - use zksync_sso_erc4337_core::erc4337::account::modular_smart_account::ModularSmartAccount; - sol! { struct SignersParams { address[] signers; @@ -2421,10 +2364,8 @@ fn create_init_data_for_deployment( } // Create initializeAccount call - let init_call = - ModularSmartAccount::initializeAccountCall { modules, data }; - Bytes::from(init_call.abi_encode()) + initialize_account_call_data_core(modules, data) } // Error type for WASM @@ -2631,23 +2572,37 @@ impl Client { #[cfg(test)] mod tests { + use super::*; use alloy::{ - primitives::{Bytes, FixedBytes, U256, address, bytes, fixed_bytes}, - providers::Provider, - rpc::types::TransactionRequest, + primitives::{ + Bytes, FixedBytes, U256, Uint, address, bytes, fixed_bytes, + }, + rpc::types::PackedUserOperation as AlloyPackedUserOperation, }; use zksync_sso_erc4337_core::{ erc4337::{ account::{ - erc7579::{Execution, module_installed::is_module_installed}, + erc7579::{ + calls::encoded_call_data, + module::{ + Module, + installed::{ + IsModuleInstalledParams, is_module_installed, + }, + }, + }, modular_smart_account::{ - add_passkey::PasskeyPayload as CorePasskeyPayload, deploy::{ DeployAccountParams, WebAuthNSigner, deploy_account, }, + passkey::add::PasskeyPayload as CorePasskeyPayload, + test_utilities::fund_account_with_default_amount, }, }, + bundler::Bundler, + entry_point::nonce::get_nonce_with_key as get_nonce_with_key_core, signer::test_utils::get_signature_from_js, + user_operation::hash::user_operation_hash::get_user_operation_hash_entry_point as get_user_operation_hash_entry_point_core, }, utils::alloy_utilities::test_utilities::{ TestInfraConfig, @@ -2720,19 +2675,15 @@ mod tests { println!("Account deployed with passkey: {:?}", account_address); // Fund the account - { - let fund_tx = TransactionRequest::default() - .to(account_address) - .value(U256::from(10000000000000000000u64)); - _ = provider.send_transaction(fund_tx).await?.get_receipt().await?; - } + fund_account_with_default_amount(account_address, provider.clone()) + .await?; // Verify passkey module is installed - let is_installed = is_module_installed( - webauthn_module, - account_address, - provider.clone(), - ) + let is_installed = is_module_installed(IsModuleInstalledParams { + module: Module::webauthn_validator(webauthn_module), + account: account_address, + provider: provider.clone(), + }) .await?; eyre::ensure!(is_installed, "WebAuthn module is not installed"); @@ -2743,32 +2694,19 @@ mod tests { println!("\nTesting two-step passkey flow (manual approach)..."); - use alloy::rpc::types::erc4337::PackedUserOperation as AlloyPackedUserOperation; - use zksync_sso_erc4337_core::erc4337::{ - account::{ - erc7579::calls::encode_calls, - modular_smart_account::nonce::get_nonce, - }, - bundler::Bundler, - entry_point::EntryPoint::PackedUserOperation, - user_operation::hash::v08::get_user_operation_hash_entry_point, - }; - - let call = Execution { - target: account_address, - value: U256::from(1), - data: Bytes::default(), - }; - let calls = vec![call]; - let call_data: Bytes = encode_calls(calls).into(); - - let nonce_key = alloy::primitives::Uint::from(0); - let nonce = get_nonce( - entry_point_address, + let call_data: Bytes = encoded_call_data( account_address, - nonce_key, - &provider, - ) + Some(Bytes::default()), + Some(U256::from(1)), + ); + + let nonce_key = Uint::from(0); + let nonce = get_nonce_with_key_core(GetNonceWithKeyParams { + sender: account_address, + entry_point: entry_point_address, + key: nonce_key, + provider: provider.clone(), + }) .await?; // Create stub signature for gas estimation @@ -2833,7 +2771,7 @@ mod tests { signature: user_op.signature.clone(), }; - let hash = get_user_operation_hash_entry_point( + let hash = get_user_operation_hash_entry_point_core( &packed_user_op, &entry_point_address, provider.clone(),