From b824eb2326c0d4a3bdc1293171c7d6112cbc25ac Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 11 Jul 2023 20:59:11 -0300 Subject: [PATCH 01/26] Add withdraw request builder --- src/zks_wallet/requests/withdraw_request.rs | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/zks_wallet/requests/withdraw_request.rs diff --git a/src/zks_wallet/requests/withdraw_request.rs b/src/zks_wallet/requests/withdraw_request.rs new file mode 100644 index 0000000..8626d6e --- /dev/null +++ b/src/zks_wallet/requests/withdraw_request.rs @@ -0,0 +1,29 @@ +use ethers::types::{Address, U256}; +use std::fmt::Debug; + +#[derive(Clone, Debug)] +pub struct WithdrawRequest { + pub amount: U256, + pub to: Option
, + pub from: Option
, +} + +impl WithdrawRequest { + pub fn with(amount: U256) -> Self { + Self { + amount, + to: None, + from: None, + } + } + + pub fn to(mut self, to: Address) -> Self { + self.to = Some(to); + self + } + + pub fn from(mut self, from: Address) -> Self { + self.from = Some(from); + self + } +} From 710c4b70b86eabe023198ca20ac2923699fb56dc Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 11 Jul 2023 20:59:44 -0300 Subject: [PATCH 02/26] New module for specific transaction requests --- src/lib.rs | 4 +-- src/zks_wallet/mod.rs | 6 ++-- .../{wallet => requests}/deposit_request.rs | 2 ++ src/zks_wallet/requests/mod.rs | 2 ++ src/zks_wallet/wallet.rs | 35 ++++++++----------- 5 files changed, 24 insertions(+), 25 deletions(-) rename src/zks_wallet/{wallet => requests}/deposit_request.rs (98%) create mode 100644 src/zks_wallet/requests/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 94fbc13..b93e073 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,9 +46,9 @@ pub mod contracts; pub mod eip712; pub mod zks_provider; pub mod zks_utils; - pub mod zks_wallet; -pub use zks_wallet::{DepositRequest, ZKSWallet, ZKSWalletError}; + +pub use zks_wallet::{ZKSWallet, ZKSWalletError}; // For macro expansions only, not public API. #[allow(unused_extern_crates)] diff --git a/src/zks_wallet/mod.rs b/src/zks_wallet/mod.rs index 312f5e8..faee3e9 100644 --- a/src/zks_wallet/mod.rs +++ b/src/zks_wallet/mod.rs @@ -1,11 +1,13 @@ mod errors; pub use errors::ZKSWalletError; +mod requests; +pub use requests::{deposit_request::DepositRequest, withdraw_request::WithdrawRequest}; + mod wallet; -use ethers::types::U256; -pub use wallet::deposit_request::DepositRequest; pub use wallet::ZKSWallet; +use ethers::types::U256; pub struct Overrides { pub value: Option, } diff --git a/src/zks_wallet/wallet/deposit_request.rs b/src/zks_wallet/requests/deposit_request.rs similarity index 98% rename from src/zks_wallet/wallet/deposit_request.rs rename to src/zks_wallet/requests/deposit_request.rs index 7c76adf..324e5e3 100644 --- a/src/zks_wallet/wallet/deposit_request.rs +++ b/src/zks_wallet/requests/deposit_request.rs @@ -16,6 +16,8 @@ fn default_l2_gas_limit() -> U256 { fn default_gas_per_pubdata_byte() -> U256 { DEPOSIT_GAS_PER_PUBDATA_LIMIT.into() } + +#[derive(Clone, Debug)] pub struct DepositRequest { pub amount: U256, pub to: Option
, diff --git a/src/zks_wallet/requests/mod.rs b/src/zks_wallet/requests/mod.rs new file mode 100644 index 0000000..e0d93d6 --- /dev/null +++ b/src/zks_wallet/requests/mod.rs @@ -0,0 +1,2 @@ +pub mod deposit_request; +pub mod withdraw_request; diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index f593911..8ccd14c 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -1,8 +1,4 @@ -pub mod deposit_request; - -use self::deposit_request::DepositRequest; - -use super::{Overrides, ZKSWalletError}; +use super::{DepositRequest, Overrides, WithdrawRequest, ZKSWalletError}; use crate::{ contracts::main_contract::{MainContract, MainContractInstance}, eip712::Eip712Transaction, @@ -30,10 +26,11 @@ use ethers::{ use serde_json::Value; use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr, sync::Arc}; +#[derive(Clone, Debug)] pub struct ZKSWallet where - M: Middleware, - D: PrehashSigner<(RecoverableSignature, RecoveryId)>, + M: Middleware + Clone, + D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Clone, { /// Eth provider pub eth_provider: Option>>>, @@ -44,7 +41,7 @@ where impl ZKSWallet where - M: Middleware + 'static, + M: Middleware + 'static + Clone, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Sync + Send + Clone, { pub fn new( @@ -509,8 +506,7 @@ where pub async fn withdraw( &self, - amount: U256, - to: Address, + request: &WithdrawRequest, ) -> Result> where M: ZKSProvider, @@ -525,6 +521,7 @@ where ZKSWalletError::CustomError(format!("failed to parse contract address: {error}")) })?; let function_signature = "function withdraw(address _l1Receiver) external payable override"; + let to: Address = request.to.unwrap_or(self.l1_address()); let response: (Vec, H256) = era_provider .send_eip712( &self.l2_wallet, @@ -532,7 +529,7 @@ where function_signature, Some([format!("{to:?}")].into()), Some(Overrides { - value: Some(amount), + value: Some(request.amount), }), ) .await?; @@ -685,8 +682,7 @@ mod zks_signer_tests { use crate::test_utils::*; use crate::zks_provider::ZKSProvider; use crate::zks_utils::{ERA_CHAIN_ID, ETH_CHAIN_ID}; - use crate::zks_wallet::wallet::deposit_request::DepositRequest; - use crate::zks_wallet::ZKSWallet; + use crate::zks_wallet::{DepositRequest, WithdrawRequest, ZKSWallet}; use ethers::abi::Tokenize; use ethers::providers::Middleware; use ethers::signers::{LocalWallet, Signer}; @@ -1060,10 +1056,8 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); - let tx_receipt = zk_wallet - .withdraw(amount_to_withdraw, zk_wallet.l1_address()) - .await - .unwrap(); + let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); + let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); assert_eq!( 1, tx_receipt.status.unwrap().as_u64(), @@ -1159,10 +1153,9 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); - let tx_receipt = zk_wallet - .withdraw(amount_to_withdraw, zk_wallet.l1_address()) - .await - .unwrap(); + let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); + let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); + assert_eq!( 1, tx_receipt.status.unwrap().as_u64(), From ec2803070fa3c93768463873f1440449f48fa6b5 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 11 Jul 2023 21:00:03 -0300 Subject: [PATCH 03/26] Improve return type on send functions --- src/zks_provider/mod.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 2594d22..9be0c23 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -759,14 +759,10 @@ impl ZKSProvider for Provider

{ ) .await?; - let transaction_receipt = pending_transaction - .await? - .ok_or(ProviderError::CustomError( - "no transaction receipt".to_owned(), - ))?; + let transaction_hash = pending_transaction.tx_hash(); // TODO: decode function output. - Ok((Vec::new(), transaction_receipt.transaction_hash)) + Ok((Vec::new(), transaction_hash)) } async fn send( @@ -790,14 +786,9 @@ impl ZKSProvider for Provider

{ ) .await?; let pending_transaction = self.send_transaction(tx, None).await?; + let transaction_hash = pending_transaction.tx_hash(); - let transaction_receipt = pending_transaction - .await? - .ok_or(ProviderError::CustomError( - "no transaction receipt".to_owned(), - ))?; - - Ok((Vec::new(), transaction_receipt.transaction_hash)) + Ok((Vec::new(), transaction_hash)) } async fn wait_for_finalize( From 8199608395ff386e9fdcb91b839dee69b439924d Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Wed, 12 Jul 2023 12:25:32 -0300 Subject: [PATCH 04/26] Delete duplicated code to get providers --- src/zks_wallet/wallet.rs | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index 8ccd14c..07744a3 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -156,10 +156,7 @@ where where M: ZKSProvider, { - let era_provider = match &self.era_provider { - Some(era_provider) => era_provider, - None => return Err(ZKSWalletError::CustomError("no era provider".to_owned())), - }; + let era_provider = self.get_era_provider()?; let mut transfer_request = Eip1559TransactionRequest::new() .from(self.l2_address()) @@ -193,10 +190,7 @@ where where M: ZKSProvider, { - let era_provider = match &self.era_provider { - Some(era_provider) => era_provider, - None => return Err(ZKSWalletError::CustomError("no era provider".to_owned())), - }; + let era_provider = self.get_era_provider()?; let mut transfer_request = Eip712TransactionRequest::new() .from(self.l2_address()) @@ -315,10 +309,7 @@ where M: ZKSProvider, T: Tokenizable, { - let era_provider = match &self.era_provider { - Some(era_provider) => era_provider, - None => return Err(ZKSWalletError::CustomError("no era provider".to_owned())), - }; + let era_provider = self.get_era_provider()?; let custom_data = Eip712Meta::new().factory_deps({ let mut factory_deps = Vec::new(); @@ -415,10 +406,7 @@ where where M: ZKSProvider, { - let era_provider = match &self.era_provider { - Some(era_provider) => era_provider, - None => return Err(ZKSWalletError::CustomError("no era provider".to_owned())), - }; + let era_provider = self.get_era_provider()?; let custom_data = Eip712Meta::new().factory_deps({ let mut factory_deps = Vec::new(); @@ -511,10 +499,7 @@ where where M: ZKSProvider, { - let era_provider = match &self.era_provider { - Some(era_provider) => era_provider, - None => return Err(ZKSWalletError::CustomError("no era provider".to_owned())), - }; + let era_provider = self.get_era_provider()?; let contract_address = Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).map_err(|error| { @@ -553,14 +538,8 @@ where where M: ZKSProvider, { - let (era_provider, eth_provider) = match (&self.era_provider, &self.eth_provider) { - (Some(era_provider), Some(eth_provider)) => (era_provider, eth_provider), - _ => { - return Err(ZKSWalletError::CustomError( - "Both era and eth providers are necessary".to_owned(), - )) - } - }; + let era_provider = self.get_era_provider()?; + let eth_provider = self.get_eth_provider()?; let withdrawal_receipt = era_provider.get_transaction_receipt(tx_hash).await?.ok_or( ZKSWalletError::CustomError("Error getting transaction receipt of withdraw".to_owned()), From 2ef7d7a14d622b7e9186fce7e58d4738feeb7afe Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Wed, 12 Jul 2023 18:51:27 -0300 Subject: [PATCH 05/26] Add function to provider to send eip712 transactions --- src/zks_provider/mod.rs | 71 +++++++++++++++++++++ src/zks_wallet/requests/withdraw_request.rs | 14 +++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 9be0c23..386a386 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -221,6 +221,15 @@ pub trait ZKSProvider { function_signature: &str, function_parameters: Option>, ) -> Result, ProviderError>; + + async fn send_transaction_eip712( + &self, + wallet: &Wallet, + transaction: T, + ) -> Result<(Vec, H256), ProviderError> + where + T: TryInto + Send + Sync + Serialize + Debug, + D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync; } #[async_trait] @@ -448,6 +457,20 @@ impl ZKSProvider for SignerMiddleware( + &self, + wallet: &Wallet, + transaction: T, + ) -> Result<(Vec, H256), ProviderError> + where + T: TryInto + Sync + Send + Serialize + Debug, + D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, + { + self.inner() + .send_transaction_eip712(wallet, transaction) + .await + } + async fn wait_for_finalize( &self, transaction_receipt: TransactionReceipt, @@ -685,6 +708,54 @@ impl ZKSProvider for Provider

{ .await } + async fn send_transaction_eip712( + &self, + wallet: &Wallet, + transaction: T, + ) -> Result<(Vec, H256), ProviderError> + where + T: TryInto + Sync + Send + Debug + Serialize, + D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, + { + let mut request: Eip712TransactionRequest = transaction.try_into().map_err(|_e| { + ProviderError::CustomError(format!("error on send_transaction_eip712")) + })?; + request = request + .chain_id(wallet.chain_id()) + .nonce(self.get_transaction_count(wallet.address(), None).await?) + .gas_price(self.get_gas_price().await?) + .max_fee_per_gas(self.get_gas_price().await?); + + let fee = self.estimate_fee(request.clone()).await?; + request = request + .max_priority_fee_per_gas(fee.max_priority_fee_per_gas) + .max_fee_per_gas(fee.max_fee_per_gas) + .gas_limit(fee.gas_limit); + + let signable_data: Eip712Transaction = request + .clone() + .try_into() + .map_err(|e: Eip712Error| ProviderError::CustomError(e.to_string()))?; + let signature: Signature = wallet + .sign_typed_data(&signable_data) + .await + .map_err(|e| ProviderError::CustomError(format!("error signing transaction: {e}")))?; + request = request.custom_data(Eip712Meta::new().custom_signature(signature.to_vec())); + + let pending_transaction = self + .send_raw_transaction( + [&[EIP712_TX_TYPE], &*request.rlp_unsigned()] + .concat() + .into(), + ) + .await?; + + let transaction_hash = pending_transaction.tx_hash(); + + // TODO: decode function output. + Ok((Vec::new(), transaction_hash)) + } + async fn send_eip712( &self, wallet: &Wallet, diff --git a/src/zks_wallet/requests/withdraw_request.rs b/src/zks_wallet/requests/withdraw_request.rs index 8626d6e..6b53184 100644 --- a/src/zks_wallet/requests/withdraw_request.rs +++ b/src/zks_wallet/requests/withdraw_request.rs @@ -1,6 +1,8 @@ -use ethers::types::{Address, U256}; +use ethers::types::{transaction::eip712::Eip712Error, Address, U256}; use std::fmt::Debug; +use crate::eip712::Eip712TransactionRequest; + #[derive(Clone, Debug)] pub struct WithdrawRequest { pub amount: U256, @@ -27,3 +29,13 @@ impl WithdrawRequest { self } } + +// impl TryFrom for WithdrawRequest { +// type Error = Eip712Error; + +// fn try_from(value: Eip712TransactionRequest) -> Result { +// Eip712TransactionRequest { + +// } +// } +// } From 981cfe365e362cd189217f2abdcd11dc2e74d214 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 13 Jul 2023 19:10:12 -0300 Subject: [PATCH 06/26] Add from trait for eip712 transactions with every request type --- src/eip712/transaction_request.rs | 47 +++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/eip712/transaction_request.rs b/src/eip712/transaction_request.rs index 7184e17..67b6dad 100644 --- a/src/eip712/transaction_request.rs +++ b/src/eip712/transaction_request.rs @@ -1,10 +1,16 @@ +use std::str::FromStr; + use super::{rlp_append_option, Eip712Meta}; use crate::{ - zks_utils::{EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS}, - zks_wallet::Overrides, + zks_utils::{self, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS}, + zks_wallet::{Overrides, TransferRequest, WithdrawRequest}, }; use ethers::{ - types::{transaction::eip2930::AccessList, Address, Bytes, Signature, U256, U64}, + abi::HumanReadableParser, + types::{ + transaction::{eip2930::AccessList, eip712::Eip712Error}, + Address, Bytes, Signature, U256, U64, + }, utils::rlp::{Encodable, RlpStream}, }; use serde::{Deserialize, Serialize}; @@ -229,3 +235,38 @@ impl Default for Eip712TransactionRequest { } } } + +impl TryFrom for Eip712TransactionRequest { + type Error = Eip712Error; + + fn try_from(request: WithdrawRequest) -> Result { + let contract_address = Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).unwrap(); + let function_signature = "function withdraw(address _l1Receiver) external payable override"; + let function = HumanReadableParser::parse_function(function_signature).unwrap(); + let function_args = function + .decode_input( + &zks_utils::encode_args(&function, &[format!("{:?}", request.to)]).unwrap(), + ) + .unwrap(); + let data: Bytes = function.encode_input(&function_args).unwrap().into(); + + Ok(Eip712TransactionRequest::new() + .r#type(EIP712_TX_TYPE) + .to(contract_address) + .value(request.amount) + .from(request.from) + .data(data)) + } +} + +impl TryFrom for Eip712TransactionRequest { + type Error = Eip712Error; + + fn try_from(request: TransferRequest) -> Result { + Ok(Eip712TransactionRequest::new() + .r#type(EIP712_TX_TYPE) + .to(request.to) + .value(request.amount) + .from(request.from)) + } +} From c6693888d25fdaee937b99a0d607676c78df4d1d Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 13 Jul 2023 19:11:08 -0300 Subject: [PATCH 07/26] Revert returning transaction hash from send functions --- src/zks_provider/mod.rs | 49 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 386a386..8a8e910 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -228,7 +228,7 @@ pub trait ZKSProvider { transaction: T, ) -> Result<(Vec, H256), ProviderError> where - T: TryInto + Send + Sync + Serialize + Debug, + T: TryInto + Send + Sync + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync; } @@ -463,7 +463,7 @@ impl ZKSProvider for SignerMiddleware Result<(Vec, H256), ProviderError> where - T: TryInto + Sync + Send + Serialize + Debug, + T: TryInto + Sync + Send + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, { self.inner() @@ -714,13 +714,15 @@ impl ZKSProvider for Provider

{ transaction: T, ) -> Result<(Vec, H256), ProviderError> where - T: TryInto + Sync + Send + Debug + Serialize, + T: TryInto + Sync + Send + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, { let mut request: Eip712TransactionRequest = transaction.try_into().map_err(|_e| { ProviderError::CustomError(format!("error on send_transaction_eip712")) })?; + request = request + .from(wallet.address()) .chain_id(wallet.chain_id()) .nonce(self.get_transaction_count(wallet.address(), None).await?) .gas_price(self.get_gas_price().await?) @@ -731,7 +733,6 @@ impl ZKSProvider for Provider

{ .max_priority_fee_per_gas(fee.max_priority_fee_per_gas) .max_fee_per_gas(fee.max_fee_per_gas) .gas_limit(fee.gas_limit); - let signable_data: Eip712Transaction = request .clone() .try_into() @@ -750,10 +751,14 @@ impl ZKSProvider for Provider

{ ) .await?; - let transaction_hash = pending_transaction.tx_hash(); + let transaction_receipt = pending_transaction + .await? + .ok_or(ProviderError::CustomError( + "no transaction receipt".to_owned(), + ))?; // TODO: decode function output. - Ok((Vec::new(), transaction_hash)) + Ok((Vec::new(), transaction_receipt.transaction_hash)) } async fn send_eip712( @@ -830,10 +835,14 @@ impl ZKSProvider for Provider

{ ) .await?; - let transaction_hash = pending_transaction.tx_hash(); + let transaction_receipt = pending_transaction + .await? + .ok_or(ProviderError::CustomError( + "no transaction receipt".to_owned(), + ))?; // TODO: decode function output. - Ok((Vec::new(), transaction_hash)) + Ok((Vec::new(), transaction_receipt.transaction_hash)) } async fn send( @@ -1019,7 +1028,7 @@ mod tests { test_utils::*, zks_provider::{types::TracerConfig, ZKSProvider}, zks_utils::ERA_CHAIN_ID, - zks_wallet::ZKSWallet, + zks_wallet::{TransferRequest, ZKSWallet}, }; use ethers::{ abi::Tokenize, @@ -1392,12 +1401,13 @@ mod tests { let era_provider = era_provider(); let zk_wallet = ZKSWallet::new(local_wallet(), None, Some(era_signer()), None).unwrap(); + let transfer_request = TransferRequest::with( + 1_u64.into(), + Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), + ) + .from(zk_wallet.l2_address()); let transaction_hash = zk_wallet - .transfer( - Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), - 1_u64.into(), - None, - ) + .transfer(transfer_request, None) .await .unwrap() .transaction_hash; @@ -1784,12 +1794,13 @@ mod tests { let zk_wallet = ZKSWallet::new(local_wallet(), None, Some(era_signer.clone()), None).unwrap(); + let transfer_request = TransferRequest::with( + 1_u64.into(), + Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), + ) + .from(zk_wallet.l2_address()); let transaction_hash = zk_wallet - .transfer( - Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), - 1_i32.into(), - None, - ) + .transfer(transfer_request, None) .await .unwrap() .transaction_hash; From bd40b5bb74c06eeef028322988f0b46b251662f6 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 13 Jul 2023 19:11:25 -0300 Subject: [PATCH 08/26] Add transfer request type --- src/zks_wallet/mod.rs | 5 ++- src/zks_wallet/requests/mod.rs | 1 + src/zks_wallet/requests/transfer_request.rs | 39 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/zks_wallet/requests/transfer_request.rs diff --git a/src/zks_wallet/mod.rs b/src/zks_wallet/mod.rs index faee3e9..88c25b0 100644 --- a/src/zks_wallet/mod.rs +++ b/src/zks_wallet/mod.rs @@ -2,7 +2,10 @@ mod errors; pub use errors::ZKSWalletError; mod requests; -pub use requests::{deposit_request::DepositRequest, withdraw_request::WithdrawRequest}; +pub use requests::{ + deposit_request::DepositRequest, transfer_request::TransferRequest, + withdraw_request::WithdrawRequest, +}; mod wallet; pub use wallet::ZKSWallet; diff --git a/src/zks_wallet/requests/mod.rs b/src/zks_wallet/requests/mod.rs index e0d93d6..12c9560 100644 --- a/src/zks_wallet/requests/mod.rs +++ b/src/zks_wallet/requests/mod.rs @@ -1,2 +1,3 @@ pub mod deposit_request; +pub mod transfer_request; pub mod withdraw_request; diff --git a/src/zks_wallet/requests/transfer_request.rs b/src/zks_wallet/requests/transfer_request.rs new file mode 100644 index 0000000..e51439d --- /dev/null +++ b/src/zks_wallet/requests/transfer_request.rs @@ -0,0 +1,39 @@ +use ethers::types::{ + transaction::{eip1559::Eip1559RequestError, eip2718::TypedTransaction}, + Address, Bytes, Eip1559TransactionRequest, U256, +}; +use std::fmt::Debug; + +use crate::zks_utils::ERA_CHAIN_ID; + +#[derive(Clone, Debug)] +pub struct TransferRequest { + pub amount: U256, + pub to: Address, + pub from: Address, +} + +impl TransferRequest { + pub fn with(amount: U256, to: Address) -> Self { + Self { + amount, + to, + from: Default::default(), + } + } + + pub fn from(mut self, from: Address) -> Self { + self.from = from; + self + } +} + +impl From for Eip1559TransactionRequest { + fn from(request: TransferRequest) -> Eip1559TransactionRequest { + Eip1559TransactionRequest::new() + .to(request.to) + .value(request.amount) + .from(request.from) + .chain_id(ERA_CHAIN_ID) + } +} From 01789f839e221d596451eba137c89c3a2b6b3fe5 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 13 Jul 2023 19:11:36 -0300 Subject: [PATCH 09/26] Add defaults for withdraw request --- src/zks_wallet/requests/withdraw_request.rs | 26 ++++++--------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/zks_wallet/requests/withdraw_request.rs b/src/zks_wallet/requests/withdraw_request.rs index 6b53184..92c189f 100644 --- a/src/zks_wallet/requests/withdraw_request.rs +++ b/src/zks_wallet/requests/withdraw_request.rs @@ -1,41 +1,29 @@ -use ethers::types::{transaction::eip712::Eip712Error, Address, U256}; +use ethers::types::{Address, Bytes, U256}; use std::fmt::Debug; -use crate::eip712::Eip712TransactionRequest; - #[derive(Clone, Debug)] pub struct WithdrawRequest { pub amount: U256, - pub to: Option

, - pub from: Option
, + pub to: Address, + pub from: Address, } impl WithdrawRequest { pub fn with(amount: U256) -> Self { Self { amount, - to: None, - from: None, + to: Default::default(), + from: Default::default(), } } pub fn to(mut self, to: Address) -> Self { - self.to = Some(to); + self.to = to; self } pub fn from(mut self, from: Address) -> Self { - self.from = Some(from); + self.from = from; self } } - -// impl TryFrom for WithdrawRequest { -// type Error = Eip712Error; - -// fn try_from(value: Eip712TransactionRequest) -> Result { -// Eip712TransactionRequest { - -// } -// } -// } From cb2f4f1796bd81c77a9d1ba0be46da97837d3a3b Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 13 Jul 2023 19:11:48 -0300 Subject: [PATCH 10/26] Update wallet to use every transaction type --- src/zks_wallet/wallet.rs | 90 ++++++++++------------------------------ 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index 07744a3..494976c 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -1,4 +1,6 @@ -use super::{DepositRequest, Overrides, WithdrawRequest, ZKSWalletError}; +use super::{ + requests::transfer_request::TransferRequest, DepositRequest, WithdrawRequest, ZKSWalletError, +}; use crate::{ contracts::main_contract::{MainContract, MainContractInstance}, eip712::Eip712Transaction, @@ -7,7 +9,7 @@ use crate::{ zks_utils::{self, CONTRACT_DEPLOYER_ADDR, EIP712_TX_TYPE, ETH_CHAIN_ID}, }; use ethers::{ - abi::{decode, Abi, ParamType, Token, Tokenizable}, + abi::{decode, Abi, ParamType, Tokenizable}, prelude::{ encode_function_data, k256::{ @@ -148,8 +150,7 @@ where pub async fn transfer( &self, - to: Address, - amount_to_transfer: U256, + request: TransferRequest, // TODO: Support multiple-token transfers. _token: Option
, ) -> Result> @@ -158,11 +159,7 @@ where { let era_provider = self.get_era_provider()?; - let mut transfer_request = Eip1559TransactionRequest::new() - .from(self.l2_address()) - .to(to) - .value(amount_to_transfer) - .chain_id(self.l2_chain_id()); + let mut transfer_request: Eip1559TransactionRequest = request.into(); let fee = era_provider.estimate_fee(transfer_request.clone()).await?; transfer_request = transfer_request.max_priority_fee_per_gas(fee.max_priority_fee_per_gas); @@ -182,8 +179,7 @@ where pub async fn transfer_eip712( &self, - to: Address, - amount_to_transfer: U256, + request: TransferRequest, // TODO: Support multiple-token transfers. _token: Option
, ) -> Result> @@ -192,37 +188,12 @@ where { let era_provider = self.get_era_provider()?; - let mut transfer_request = Eip712TransactionRequest::new() - .from(self.l2_address()) - .to(to) - .value(amount_to_transfer) - .nonce( - era_provider - .get_transaction_count(self.l2_address(), None) - .await?, - ) - .gas_price(era_provider.get_gas_price().await?); - - let fee = era_provider.estimate_fee(transfer_request.clone()).await?; - transfer_request = transfer_request - .max_priority_fee_per_gas(fee.max_priority_fee_per_gas) - .max_fee_per_gas(fee.max_fee_per_gas) - .gas_limit(fee.gas_limit); - - let signable_data: Eip712Transaction = transfer_request.clone().try_into()?; - let signature: Signature = self.l2_wallet.sign_typed_data(&signable_data).await?; - transfer_request = - transfer_request.custom_data(Eip712Meta::new().custom_signature(signature.to_vec())); - - let pending_transaction = era_provider - .send_raw_transaction( - [&[EIP712_TX_TYPE], &*transfer_request.rlp_unsigned()] - .concat() - .into(), - ) + let response = era_provider + .send_transaction_eip712(&self.l2_wallet, request) .await?; - let transaction_receipt = pending_transaction + let transaction_receipt = era_provider + .get_transaction_receipt(response.1) .await? .ok_or(ZKSWalletError::CustomError( "no transaction receipt".to_owned(), @@ -494,29 +465,14 @@ where pub async fn withdraw( &self, - request: &WithdrawRequest, + request: WithdrawRequest, ) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; - - let contract_address = - Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).map_err(|error| { - ZKSWalletError::CustomError(format!("failed to parse contract address: {error}")) - })?; - let function_signature = "function withdraw(address _l1Receiver) external payable override"; - let to: Address = request.to.unwrap_or(self.l1_address()); - let response: (Vec, H256) = era_provider - .send_eip712( - &self.l2_wallet, - contract_address, - function_signature, - Some([format!("{to:?}")].into()), - Some(Overrides { - value: Some(request.amount), - }), - ) + let response = era_provider + .send_transaction_eip712(&self.l2_wallet, request) .await?; let tx_receipt = era_provider @@ -661,7 +617,7 @@ mod zks_signer_tests { use crate::test_utils::*; use crate::zks_provider::ZKSProvider; use crate::zks_utils::{ERA_CHAIN_ID, ETH_CHAIN_ID}; - use crate::zks_wallet::{DepositRequest, WithdrawRequest, ZKSWallet}; + use crate::zks_wallet::{DepositRequest, TransferRequest, WithdrawRequest, ZKSWallet}; use ethers::abi::Tokenize; use ethers::providers::Middleware; use ethers::signers::{LocalWallet, Signer}; @@ -698,13 +654,10 @@ mod zks_signer_tests { println!("Sender balance before: {sender_balance_before}"); println!("Receiver balance before: {receiver_balance_before}"); - println!("Sender balance before: {sender_balance_before}"); - println!("Receiver balance before: {receiver_balance_before}"); - let receipt = zk_wallet - .transfer(receiver_address, amount_to_transfer, None) - .await - .unwrap(); + let transfer_request = TransferRequest::with(amount_to_transfer, receiver_address) + .from(zk_wallet.l2_address()); + let receipt = zk_wallet.transfer(transfer_request, None).await.unwrap(); assert_eq!(receipt.from, zk_wallet.l2_address()); assert_eq!(receipt.to.unwrap(), receiver_address); @@ -859,8 +812,9 @@ mod zks_signer_tests { println!("Sender balance before: {sender_balance_before}"); println!("Receiver balance before: {receiver_balance_before}"); + let transfer_request = TransferRequest::with(amount_to_transfer, receiver_address); let receipt = zk_wallet - .transfer_eip712(receiver_address, amount_to_transfer, None) + .transfer_eip712(transfer_request, None) .await .unwrap(); @@ -1036,7 +990,7 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); - let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); + let tx_receipt = zk_wallet.withdraw(withdraw_request).await.unwrap(); assert_eq!( 1, tx_receipt.status.unwrap().as_u64(), @@ -1133,7 +1087,7 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); - let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); + let tx_receipt = zk_wallet.withdraw(withdraw_request).await.unwrap(); assert_eq!( 1, From 9e9d92f642808875ac50a0cfe211005ef958f6e5 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Fri, 14 Jul 2023 17:31:36 -0300 Subject: [PATCH 11/26] Add deploy request implementation --- src/eip712/transaction_request.rs | 60 +++++++++++++++++++++-- src/zks_wallet/requests/deploy_request.rs | 37 ++++++++++++++ src/zks_wallet/requests/mod.rs | 2 + 3 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/zks_wallet/requests/deploy_request.rs diff --git a/src/eip712/transaction_request.rs b/src/eip712/transaction_request.rs index 67b6dad..81987fc 100644 --- a/src/eip712/transaction_request.rs +++ b/src/eip712/transaction_request.rs @@ -1,18 +1,21 @@ -use std::str::FromStr; +use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr}; -use super::{rlp_append_option, Eip712Meta}; +use super::{hash_bytecode, rlp_append_option, Eip712Meta}; use crate::{ - zks_utils::{self, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS}, - zks_wallet::{Overrides, TransferRequest, WithdrawRequest}, + zks_utils::{ + self, CONTRACT_DEPLOYER_ADDR, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS, + }, + zks_wallet::{DeployRequest, Overrides, TransferRequest, WithdrawRequest}, }; use ethers::{ - abi::HumanReadableParser, + abi::{Abi, HumanReadableParser}, types::{ transaction::{eip2930::AccessList, eip712::Eip712Error}, Address, Bytes, Signature, U256, U64, }, utils::rlp::{Encodable, RlpStream}, }; +use ethers_contract::encode_function_data; use serde::{Deserialize, Serialize}; // TODO: Not all the fields are optional. This was copied from the JS implementation. @@ -270,3 +273,50 @@ impl TryFrom for Eip712TransactionRequest { .from(request.from)) } } + +impl TryFrom for Eip712TransactionRequest { + type Error = Eip712Error; + + fn try_from(request: DeployRequest) -> Result { + let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + contract_deployer_path.push("src/abi/ContractDeployer.json"); + + let custom_data = Eip712Meta::new().factory_deps({ + let mut factory_deps = Vec::new(); + if let Some(factory_dependencies) = request.factory_deps { + factory_deps.extend(factory_dependencies); + } + factory_deps.push(request.contract_bytecode.clone()); + factory_deps + }); + + let contract_deployer = + Abi::load(BufReader::new(File::open(contract_deployer_path).unwrap())).unwrap(); + let create = contract_deployer.function("create").unwrap(); + + // TODO: User could provide this instead of defaulting. + let salt = [0_u8; 32]; + let bytecode_hash = hash_bytecode(&request.contract_bytecode).unwrap(); + let call_data: Bytes = match ( + request.contract_abi.constructor(), + request.constructor_parameters.is_empty(), + ) { + (None, false) => return Err(Eip712Error::FailedToEncodeStruct), + (None, true) | (Some(_), true) => Bytes::default(), + (Some(constructor), false) => { + zks_utils::encode_constructor_args(constructor, &request.constructor_parameters) + .unwrap() + .into() + } + }; + + let data = encode_function_data(create, (salt, bytecode_hash, call_data)).unwrap(); + + let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).unwrap(); + Ok(Eip712TransactionRequest::new() + .r#type(EIP712_TX_TYPE) + .to(contract_deployer_address) + .custom_data(custom_data) + .data(data)) + } +} diff --git a/src/zks_wallet/requests/deploy_request.rs b/src/zks_wallet/requests/deploy_request.rs new file mode 100644 index 0000000..26bb6db --- /dev/null +++ b/src/zks_wallet/requests/deploy_request.rs @@ -0,0 +1,37 @@ +use ethers::{abi::Abi, types::Address}; +use std::fmt::Debug; + +#[derive(Clone, Debug)] +pub struct DeployRequest { + pub contract_abi: Abi, + pub contract_bytecode: Vec, + pub constructor_parameters: Vec, + pub from: Address, + pub factory_deps: Option>>, +} + +impl DeployRequest { + pub fn with( + contract_abi: Abi, + contract_bytecode: Vec, + constructor_parameters: Vec, + ) -> Self { + Self { + contract_abi, + contract_bytecode, + constructor_parameters, + from: Default::default(), + factory_deps: None, + } + } + + pub fn from(mut self, from: Address) -> Self { + self.from = from; + self + } + + pub fn factory_deps(mut self, factory_deps: Vec>) -> Self { + self.factory_deps = Some(factory_deps); + self + } +} diff --git a/src/zks_wallet/requests/mod.rs b/src/zks_wallet/requests/mod.rs index 12c9560..f19ae5b 100644 --- a/src/zks_wallet/requests/mod.rs +++ b/src/zks_wallet/requests/mod.rs @@ -1,3 +1,5 @@ +pub mod call_request; +pub mod deploy_request; pub mod deposit_request; pub mod transfer_request; pub mod withdraw_request; From 3cbd7b0469c8c1cc06012c32586cd016ffd932b7 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Fri, 14 Jul 2023 17:32:06 -0300 Subject: [PATCH 12/26] Use deploy request in wallet deploy function --- src/zks_wallet/wallet.rs | 175 ++++++++++----------------------------- 1 file changed, 44 insertions(+), 131 deletions(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index 494976c..3ad6cee 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -1,5 +1,6 @@ use super::{ - requests::transfer_request::TransferRequest, DepositRequest, WithdrawRequest, ZKSWalletError, + requests::transfer_request::TransferRequest, DeployRequest, DepositRequest, WithdrawRequest, + ZKSWalletError, }; use crate::{ contracts::main_contract::{MainContract, MainContractInstance}, @@ -179,7 +180,7 @@ where pub async fn transfer_eip712( &self, - request: TransferRequest, + request: &TransferRequest, // TODO: Support multiple-token transfers. _token: Option
, ) -> Result> @@ -189,7 +190,7 @@ where let era_provider = self.get_era_provider()?; let response = era_provider - .send_transaction_eip712(&self.l2_wallet, request) + .send_transaction_eip712(&self.l2_wallet, request.clone()) .await?; let transaction_receipt = era_provider @@ -274,6 +275,7 @@ where &self, contract_bytecode: &[u8], contract_dependencies: Option>>, + // TODO: accept constructor parameters. _constructor_parameters: Option, ) -> Result> where @@ -369,94 +371,21 @@ where pub async fn deploy( &self, - contract_abi: Abi, - contract_bytecode: Vec, - constructor_parameters: Vec, - factory_dependencies: Option>>, + request: &DeployRequest, ) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; - let custom_data = Eip712Meta::new().factory_deps({ - let mut factory_deps = Vec::new(); - if let Some(factory_dependencies) = factory_dependencies { - factory_deps.extend(factory_dependencies); - } - factory_deps.push(contract_bytecode.clone()); - factory_deps - }); - - let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - contract_deployer_path.push("src/abi/ContractDeployer.json"); - let mut deploy_request = Eip712TransactionRequest::new() - .r#type(EIP712_TX_TYPE) - .from(self.l2_address()) - .to(Address::from_str(CONTRACT_DEPLOYER_ADDR).map_err(|e| { - ZKSWalletError::CustomError(format!("invalid contract deployer address: {e}")) - })?) - .chain_id(self.l2_chain_id()) - .nonce( - era_provider - .get_transaction_count(self.l2_address(), None) - .await?, - ) - .gas_price(era_provider.get_gas_price().await?) - .max_fee_per_gas(era_provider.get_gas_price().await?) - .data({ - let contract_deployer = Abi::load(BufReader::new( - File::open(contract_deployer_path).map_err(|e| { - ZKSWalletError::CustomError(format!( - "failed to open ContractDeployer abi: {e}" - )) - })?, - )) - .map_err(|e| { - ZKSWalletError::CustomError(format!("failed to load ContractDeployer abi: {e}")) - })?; - let create = contract_deployer.function("create").map_err(|e| { - ZKSWalletError::CustomError(format!("failed to get create function: {e}")) - })?; - // TODO: User could provide this instead of defaulting. - let salt = [0_u8; 32]; - let bytecode_hash = hash_bytecode(&contract_bytecode)?; - let call_data: Bytes = match ( - contract_abi.constructor(), - constructor_parameters.is_empty(), - ) { - (None, false) => return Err(ContractError::::ConstructorError.into()), - (None, true) | (Some(_), true) => Bytes::default(), - (Some(constructor), false) => { - zks_utils::encode_constructor_args(constructor, &constructor_parameters)? - .into() - } - }; - - encode_function_data(create, (salt, bytecode_hash, call_data))? - }) - .custom_data(custom_data.clone()); - - let fee = era_provider.estimate_fee(deploy_request.clone()).await?; - deploy_request = deploy_request - .max_priority_fee_per_gas(fee.max_priority_fee_per_gas) - .max_fee_per_gas(fee.max_fee_per_gas) - .gas_limit(fee.gas_limit); - - let signable_data: Eip712Transaction = deploy_request.clone().try_into()?; - let signature: Signature = self.l2_wallet.sign_typed_data(&signable_data).await?; - deploy_request = - deploy_request.custom_data(custom_data.custom_signature(signature.to_vec())); + let eip712_request: Eip712TransactionRequest = request.clone().try_into().unwrap(); - let pending_transaction = era_provider - .send_raw_transaction( - [&[EIP712_TX_TYPE], &*deploy_request.rlp_unsigned()] - .concat() - .into(), - ) + let response = era_provider + .send_transaction_eip712(&self.l2_wallet, eip712_request) .await?; - pending_transaction + era_provider + .get_transaction_receipt(response.1) .await? .ok_or(ZKSWalletError::CustomError( "no transaction receipt".to_owned(), @@ -465,14 +394,14 @@ where pub async fn withdraw( &self, - request: WithdrawRequest, + request: &WithdrawRequest, ) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; let response = era_provider - .send_transaction_eip712(&self.l2_wallet, request) + .send_transaction_eip712(&self.l2_wallet, request.clone()) .await?; let tx_receipt = era_provider @@ -617,7 +546,9 @@ mod zks_signer_tests { use crate::test_utils::*; use crate::zks_provider::ZKSProvider; use crate::zks_utils::{ERA_CHAIN_ID, ETH_CHAIN_ID}; - use crate::zks_wallet::{DepositRequest, TransferRequest, WithdrawRequest, ZKSWallet}; + use crate::zks_wallet::{ + CallRequest, DeployRequest, DepositRequest, TransferRequest, WithdrawRequest, ZKSWallet, + }; use ethers::abi::Tokenize; use ethers::providers::Middleware; use ethers::signers::{LocalWallet, Signer}; @@ -814,7 +745,7 @@ mod zks_signer_tests { let transfer_request = TransferRequest::with(amount_to_transfer, receiver_address); let receipt = zk_wallet - .transfer_eip712(transfer_request, None) + .transfer_eip712(&transfer_request, None) .await .unwrap(); @@ -862,15 +793,10 @@ mod zks_signer_tests { let contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy( - contract.abi, - contract.bin.to_vec(), - vec!["10".to_owned()], - None, - ) - .await - .unwrap(); + let deploy_request = + DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["10".to_owned()]) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); let deploy_result = era_provider.get_code(contract_address, None).await; @@ -893,15 +819,10 @@ mod zks_signer_tests { let contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy( - contract.abi, - contract.bin.to_vec(), - vec!["Hey".to_owned()], - None, - ) - .await - .unwrap(); + let deploy_request = + DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["Hey".to_owned()]) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); let deploy_result = era_provider.get_code(contract_address, None).await; @@ -925,15 +846,10 @@ mod zks_signer_tests { let counter_contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy( - counter_contract.abi, - counter_contract.bin.to_vec(), - vec![], - None, - ) - .await - .unwrap(); + let deploy_request = + DeployRequest::with(counter_contract.abi, counter_contract.bin.to_vec(), vec![]) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let counter_contract_address = transaction_receipt.contract_address.unwrap(); let deploy_result = era_provider.get_code(counter_contract_address, None).await; @@ -947,25 +863,22 @@ mod zks_signer_tests { let import_contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy( - import_contract.abi, - import_contract.bin.to_vec(), - vec![format!("{counter_contract_address:?}")], - None, - ) - .await - .unwrap(); + let deploy_request = DeployRequest::with( + import_contract.abi, + import_contract.bin.to_vec(), + vec![format!("{counter_contract_address:?}")], + ) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let import_contract_address = transaction_receipt.contract_address.unwrap(); - let value = ZKSProvider::call( - &era_provider, + let call_request = CallRequest::with( import_contract_address, - "getCounterValue()(uint256)", - None, - ) - .await - .unwrap(); + "getCounterValue()(uint256)".to_owned(), + ); + let value = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); assert_eq!(value, U256::from(0_u64).into_tokens()); } @@ -990,7 +903,7 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); - let tx_receipt = zk_wallet.withdraw(withdraw_request).await.unwrap(); + let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); assert_eq!( 1, tx_receipt.status.unwrap().as_u64(), @@ -1087,7 +1000,7 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); let withdraw_request = WithdrawRequest::with(amount_to_withdraw).to(zk_wallet.l1_address()); - let tx_receipt = zk_wallet.withdraw(withdraw_request).await.unwrap(); + let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); assert_eq!( 1, From 5ab4ec723492ce2eee50a40b8a9b38edce590f92 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Fri, 14 Jul 2023 17:32:23 -0300 Subject: [PATCH 13/26] Add call request implementation --- src/zks_provider/mod.rs | 154 ++++++------------------ src/zks_wallet/mod.rs | 4 +- src/zks_wallet/requests/call_request.rs | 80 ++++++++++++ 3 files changed, 122 insertions(+), 116 deletions(-) create mode 100644 src/zks_wallet/requests/call_request.rs diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 8a8e910..1202e6d 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -29,7 +29,7 @@ use crate::{ zks_utils::{ self, is_precompile, DEFAULT_GAS, EIP712_TX_TYPE, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS, }, - zks_wallet::Overrides, + zks_wallet::{CallRequest, Overrides}, }; use self::types::{ @@ -215,12 +215,7 @@ pub trait ZKSProvider { timeout_in_seconds: Option, ) -> Result; - async fn call( - &self, - contract_address: Address, - function_signature: &str, - function_parameters: Option>, - ) -> Result, ProviderError>; + async fn call(&self, request: &CallRequest) -> Result, ProviderError>; async fn send_transaction_eip712( &self, @@ -486,19 +481,8 @@ impl ZKSProvider for SignerMiddleware>, - ) -> Result, ProviderError> { - ZKSProvider::call( - self.inner(), - contract_address, - function_signature, - function_parameters, - ) - .await + async fn call(&self, request: &CallRequest) -> Result, ProviderError> { + ZKSProvider::call(self.inner(), request).await } } @@ -728,6 +712,7 @@ impl ZKSProvider for Provider

{ .gas_price(self.get_gas_price().await?) .max_fee_per_gas(self.get_gas_price().await?); + let custom_data = request.clone().custom_data; let fee = self.estimate_fee(request.clone()).await?; request = request .max_priority_fee_per_gas(fee.max_priority_fee_per_gas) @@ -741,7 +726,7 @@ impl ZKSProvider for Provider

{ .sign_typed_data(&signable_data) .await .map_err(|e| ProviderError::CustomError(format!("error signing transaction: {e}")))?; - request = request.custom_data(Eip712Meta::new().custom_signature(signature.to_vec())); + request = request.custom_data(custom_data.custom_signature(signature.to_vec())); let pending_transaction = self .send_raw_transaction( @@ -907,52 +892,9 @@ impl ZKSProvider for Provider

{ } } - async fn call( - &self, - contract_address: Address, - function_signature: &str, - function_parameters: Option>, - ) -> Result, ProviderError> { - // Note: We couldn't implement ZKSWalletError::LexerError because ethers-rs's LexerError is not exposed. - let function = if contract_address == zks_utils::ECADD_PRECOMPILE_ADDRESS { - zks_utils::ec_add_function() - } else if contract_address == zks_utils::ECMUL_PRECOMPILE_ADDRESS { - zks_utils::ec_mul_function() - } else if contract_address == zks_utils::MODEXP_PRECOMPILE_ADDRESS { - zks_utils::mod_exp_function() - } else { - HumanReadableParser::parse_function(function_signature) - .map_err(|e| ProviderError::CustomError(e.to_string()))? - }; - let function_args = if let Some(function_args) = function_parameters { - function - .decode_input( - &zks_utils::encode_args(&function, &function_args) - .map_err(|e| ProviderError::CustomError(e.to_string()))?, - ) - .map_err(|e| ProviderError::CustomError(e.to_string()))? - } else { - vec![] - }; - - log::info!("{function_args:?}"); - - let request: Eip1559TransactionRequest = - Eip1559TransactionRequest::new().to(contract_address).data( - match (!function_args.is_empty(), is_precompile(contract_address)) { - // The contract to call is a precompile with arguments. - (true, true) => encode(&function_args), - // The contract to call is a regular contract with arguments. - (true, false) => function - .encode_input(&function_args) - .map_err(|e| ProviderError::CustomError(e.to_string()))?, - // The contract to call is a precompile without arguments. - (false, true) => Default::default(), - // The contract to call is a regular contract without arguments. - (false, false) => function.short_signature().into(), - }, - ); - + async fn call(&self, request: &CallRequest) -> Result, ProviderError> { + let function = request.get_parsed_function(); + let request: Eip1559TransactionRequest = request.clone().try_into().unwrap(); let transaction: TypedTransaction = request.into(); let encoded_output = Middleware::call(self, &transaction, None).await?; @@ -1028,7 +970,7 @@ mod tests { test_utils::*, zks_provider::{types::TracerConfig, ZKSProvider}, zks_utils::ERA_CHAIN_ID, - zks_wallet::{TransferRequest, ZKSWallet}, + zks_wallet::{CallRequest, DeployRequest, TransferRequest, ZKSWallet}, }; use ethers::{ abi::Tokenize, @@ -1850,21 +1792,16 @@ mod tests { let contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy( - contract.abi, - contract.bin.to_vec(), - vec!["0".to_owned()], - None, - ) - .await - .unwrap(); + let deploy_request = + DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["0".to_owned()]) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); - let initial_value = - ZKSProvider::call(&era_provider, contract_address, "getValue()(uint256)", None) - .await - .unwrap(); + let call_request = CallRequest::with(contract_address, "getValue()(uint256)".to_owned()); + let initial_value = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); assert_eq!(initial_value, U256::from(0_i32).into_tokens()); @@ -1879,10 +1816,9 @@ mod tests { ) .await .unwrap(); - let set_value = - ZKSProvider::call(&era_provider, contract_address, "getValue()(uint256)", None) - .await - .unwrap(); + let set_value = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); assert_eq!( set_value, @@ -1899,10 +1835,9 @@ mod tests { ) .await .unwrap(); - let incremented_value = - ZKSProvider::call(&era_provider, contract_address, "getValue()(uint256)", None) - .await - .unwrap(); + let incremented_value = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); assert_eq!( incremented_value, @@ -1925,13 +1860,12 @@ mod tests { let contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy(contract.abi, contract.bin.to_vec(), vec![], None) - .await - .unwrap(); + let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); - let output = ZKSProvider::call(&era_provider, contract_address, "str_out()(string)", None) + let call_request = CallRequest::with(contract_address, "str_out()(string)".to_owned()); + let output = ZKSProvider::call(&era_provider, &call_request) .await .unwrap(); @@ -1954,29 +1888,21 @@ mod tests { let contract: CompiledContract = serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); - let transaction_receipt = zk_wallet - .deploy(contract.abi, contract.bin.to_vec(), vec![], None) - .await - .unwrap(); + let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]) + .from(zk_wallet.l2_address()); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); - let no_return_type_output = ZKSProvider::call( - &era_provider, - contract_address, - "plus_one(uint256)", - Some(vec!["1".to_owned()]), - ) - .await - .unwrap(); + let call_request = CallRequest::with(contract_address, "plus_one(uint256)".to_owned()) + .function_parameters(vec!["1".to_owned()]); + let no_return_type_output = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); - let known_return_type_output = ZKSProvider::call( - &era_provider, - contract_address, - "plus_one(uint256)(uint256)", - Some(vec!["1".to_owned()]), - ) - .await - .unwrap(); + let call_request = call_request.function_signature("plus_one(uint256)(uint256)".to_owned()); + let known_return_type_output = ZKSProvider::call(&era_provider, &call_request) + .await + .unwrap(); assert_eq!( no_return_type_output, diff --git a/src/zks_wallet/mod.rs b/src/zks_wallet/mod.rs index 88c25b0..49eaf57 100644 --- a/src/zks_wallet/mod.rs +++ b/src/zks_wallet/mod.rs @@ -3,8 +3,8 @@ pub use errors::ZKSWalletError; mod requests; pub use requests::{ - deposit_request::DepositRequest, transfer_request::TransferRequest, - withdraw_request::WithdrawRequest, + call_request::CallRequest, deploy_request::DeployRequest, deposit_request::DepositRequest, + transfer_request::TransferRequest, withdraw_request::WithdrawRequest, }; mod wallet; diff --git a/src/zks_wallet/requests/call_request.rs b/src/zks_wallet/requests/call_request.rs new file mode 100644 index 0000000..f1ea6e8 --- /dev/null +++ b/src/zks_wallet/requests/call_request.rs @@ -0,0 +1,80 @@ +use ethers::{ + abi::{encode, Function, HumanReadableParser}, + types::{transaction::eip1559::Eip1559RequestError, Address, Eip1559TransactionRequest}, +}; +use std::fmt::Debug; + +use crate::zks_utils::{self, is_precompile}; + +#[derive(Clone, Debug)] +pub struct CallRequest { + pub to: Address, + pub function_signature: String, + pub function_parameters: Option>, +} + +impl CallRequest { + pub fn with(to: Address, function_signature: String) -> Self { + Self { + to, + function_signature, + function_parameters: None, + } + } + + pub fn function_parameters(mut self, function_parameters: Vec) -> Self { + self.function_parameters = Some(function_parameters); + self + } + + pub fn to(mut self, to: Address) -> Self { + self.to = to; + self + } + + pub fn function_signature(mut self, function_signature: String) -> Self { + self.function_signature = function_signature; + self + } + + pub fn get_parsed_function(&self) -> Function { + let function = if self.to == zks_utils::ECADD_PRECOMPILE_ADDRESS { + zks_utils::ec_add_function() + } else if self.to == zks_utils::ECMUL_PRECOMPILE_ADDRESS { + zks_utils::ec_mul_function() + } else if self.to == zks_utils::MODEXP_PRECOMPILE_ADDRESS { + zks_utils::mod_exp_function() + } else { + HumanReadableParser::parse_function(&self.function_signature).unwrap() + }; + function + } +} + +impl TryFrom for Eip1559TransactionRequest { + type Error = Eip1559RequestError; + + fn try_from(request: CallRequest) -> Result { + let function = request.get_parsed_function(); + let function_args = if let Some(function_args) = request.function_parameters { + function + .decode_input(&zks_utils::encode_args(&function, &function_args).unwrap()) + .unwrap() + } else { + vec![] + }; + + let data = match (!function_args.is_empty(), is_precompile(request.to)) { + // The contract to call is a precompile with arguments. + (true, true) => encode(&function_args), + // The contract to call is a regular contract with arguments. + (true, false) => function.encode_input(&function_args).unwrap(), + // The contract to call is a precompile without arguments. + (false, true) => Default::default(), + // The contract to call is a regular contract without arguments. + (false, false) => function.short_signature().into(), + }; + + Ok(Eip1559TransactionRequest::new().to(request.to).data(data)) + } +} From b29a21a8859fa3ffe91eba325c7f259b6f14e34c Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Fri, 14 Jul 2023 17:32:34 -0300 Subject: [PATCH 14/26] Fix unused imports --- src/zks_wallet/requests/transfer_request.rs | 5 +---- src/zks_wallet/requests/withdraw_request.rs | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/zks_wallet/requests/transfer_request.rs b/src/zks_wallet/requests/transfer_request.rs index e51439d..67218c9 100644 --- a/src/zks_wallet/requests/transfer_request.rs +++ b/src/zks_wallet/requests/transfer_request.rs @@ -1,7 +1,4 @@ -use ethers::types::{ - transaction::{eip1559::Eip1559RequestError, eip2718::TypedTransaction}, - Address, Bytes, Eip1559TransactionRequest, U256, -}; +use ethers::types::{Address, Eip1559TransactionRequest, U256}; use std::fmt::Debug; use crate::zks_utils::ERA_CHAIN_ID; diff --git a/src/zks_wallet/requests/withdraw_request.rs b/src/zks_wallet/requests/withdraw_request.rs index 92c189f..b88c552 100644 --- a/src/zks_wallet/requests/withdraw_request.rs +++ b/src/zks_wallet/requests/withdraw_request.rs @@ -1,6 +1,7 @@ -use ethers::types::{Address, Bytes, U256}; use std::fmt::Debug; +use ethers::types::{Address, U256}; + #[derive(Clone, Debug)] pub struct WithdrawRequest { pub amount: U256, From 01d399ff3af53a296f763753a1314e1324eddc2c Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Fri, 14 Jul 2023 18:52:31 -0300 Subject: [PATCH 15/26] Fix deploy test --- src/zks_wallet/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index 3ad6cee..ed5ecce 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -796,7 +796,7 @@ mod zks_signer_tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["10".to_owned()]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(deploy_request).await.unwrap(); + let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); let contract_address = transaction_receipt.contract_address.unwrap(); let deploy_result = era_provider.get_code(contract_address, None).await; From 85818bef81d05459a724a7ffd382d31102546264 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 18 Jul 2023 11:49:54 -0300 Subject: [PATCH 16/26] Fix clippy lints --- src/eip712/transaction_request.rs | 72 ++++++++++++++----------- src/zks_provider/mod.rs | 17 +++--- src/zks_wallet/mod.rs | 2 +- src/zks_wallet/requests/call_request.rs | 35 ++++++------ src/zks_wallet/wallet.rs | 4 +- 5 files changed, 72 insertions(+), 58 deletions(-) diff --git a/src/eip712/transaction_request.rs b/src/eip712/transaction_request.rs index 81987fc..0cd4801 100644 --- a/src/eip712/transaction_request.rs +++ b/src/eip712/transaction_request.rs @@ -5,14 +5,11 @@ use crate::{ zks_utils::{ self, CONTRACT_DEPLOYER_ADDR, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS, }, - zks_wallet::{DeployRequest, Overrides, TransferRequest, WithdrawRequest}, + zks_wallet::{DeployRequest, Overrides, TransferRequest, WithdrawRequest, ZKRequestError}, }; use ethers::{ - abi::{Abi, HumanReadableParser}, - types::{ - transaction::{eip2930::AccessList, eip712::Eip712Error}, - Address, Bytes, Signature, U256, U64, - }, + abi::{Abi, HumanReadableParser, ParseError}, + types::{transaction::eip2930::AccessList, Address, Bytes, Signature, U256, U64}, utils::rlp::{Encodable, RlpStream}, }; use ethers_contract::encode_function_data; @@ -240,18 +237,21 @@ impl Default for Eip712TransactionRequest { } impl TryFrom for Eip712TransactionRequest { - type Error = Eip712Error; + type Error = ZKRequestError; fn try_from(request: WithdrawRequest) -> Result { - let contract_address = Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).unwrap(); + let contract_address = + Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).map_err(|e| { + ZKRequestError::CustomError(format!("Error getting L2 ETH token address {e:?}")) + })?; let function_signature = "function withdraw(address _l1Receiver) external payable override"; - let function = HumanReadableParser::parse_function(function_signature).unwrap(); - let function_args = function - .decode_input( - &zks_utils::encode_args(&function, &[format!("{:?}", request.to)]).unwrap(), - ) - .unwrap(); - let data: Bytes = function.encode_input(&function_args).unwrap().into(); + let function = HumanReadableParser::parse_function(function_signature) + .map_err(ParseError::LexerError)?; + let function_args = function.decode_input(&zks_utils::encode_args( + &function, + &[format!("{:?}", request.to)], + )?)?; + let data: Bytes = function.encode_input(&function_args)?.into(); Ok(Eip712TransactionRequest::new() .r#type(EIP712_TX_TYPE) @@ -262,20 +262,18 @@ impl TryFrom for Eip712TransactionRequest { } } -impl TryFrom for Eip712TransactionRequest { - type Error = Eip712Error; - - fn try_from(request: TransferRequest) -> Result { - Ok(Eip712TransactionRequest::new() +impl From for Eip712TransactionRequest { + fn from(request: TransferRequest) -> Self { + Eip712TransactionRequest::new() .r#type(EIP712_TX_TYPE) .to(request.to) .value(request.amount) - .from(request.from)) + .from(request.from) } } impl TryFrom for Eip712TransactionRequest { - type Error = Eip712Error; + type Error = ZKRequestError; fn try_from(request: DeployRequest) -> Result { let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -290,29 +288,41 @@ impl TryFrom for Eip712TransactionRequest { factory_deps }); - let contract_deployer = - Abi::load(BufReader::new(File::open(contract_deployer_path).unwrap())).unwrap(); - let create = contract_deployer.function("create").unwrap(); + let contract_deployer = Abi::load(BufReader::new( + File::open(contract_deployer_path).map_err(|e| { + ZKRequestError::CustomError(format!( + "Error opening contract deployer abi file {e:?}" + )) + })?, + ))?; + let create = contract_deployer.function("create")?; // TODO: User could provide this instead of defaulting. let salt = [0_u8; 32]; - let bytecode_hash = hash_bytecode(&request.contract_bytecode).unwrap(); + let bytecode_hash = hash_bytecode(&request.contract_bytecode).map_err(|e| { + ZKRequestError::CustomError(format!("Error hashing contract bytecode {e:?}")) + })?; let call_data: Bytes = match ( request.contract_abi.constructor(), request.constructor_parameters.is_empty(), ) { - (None, false) => return Err(Eip712Error::FailedToEncodeStruct), + (None, false) => { + return Err(ZKRequestError::CustomError( + "Constructor not present".to_owned(), + )) + } (None, true) | (Some(_), true) => Bytes::default(), (Some(constructor), false) => { - zks_utils::encode_constructor_args(constructor, &request.constructor_parameters) - .unwrap() + zks_utils::encode_constructor_args(constructor, &request.constructor_parameters)? .into() } }; - let data = encode_function_data(create, (salt, bytecode_hash, call_data)).unwrap(); + let data = encode_function_data(create, (salt, bytecode_hash, call_data))?; - let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).unwrap(); + let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).map_err(|e| { + ZKRequestError::CustomError(format!("Error getting contract deployer address {e:?}")) + })?; Ok(Eip712TransactionRequest::new() .r#type(EIP712_TX_TYPE) .to(contract_deployer_address) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 1202e6d..5f819da 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use ethers::{ - abi::{encode, HumanReadableParser, Token, Tokenize}, + abi::{HumanReadableParser, Token, Tokenize}, prelude::{ k256::{ ecdsa::{RecoveryId, Signature as RecoverableSignature}, @@ -26,9 +26,7 @@ use types::Fee; use crate::{ eip712::{Eip712Meta, Eip712Transaction, Eip712TransactionRequest}, - zks_utils::{ - self, is_precompile, DEFAULT_GAS, EIP712_TX_TYPE, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS, - }, + zks_utils::{self, DEFAULT_GAS, EIP712_TX_TYPE, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS}, zks_wallet::{CallRequest, Overrides}, }; @@ -702,7 +700,7 @@ impl ZKSProvider for Provider

{ D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, { let mut request: Eip712TransactionRequest = transaction.try_into().map_err(|_e| { - ProviderError::CustomError(format!("error on send_transaction_eip712")) + ProviderError::CustomError("error on send_transaction_eip712".to_owned()) })?; request = request @@ -893,8 +891,13 @@ impl ZKSProvider for Provider

{ } async fn call(&self, request: &CallRequest) -> Result, ProviderError> { - let function = request.get_parsed_function(); - let request: Eip1559TransactionRequest = request.clone().try_into().unwrap(); + let function = request + .get_parsed_function() + .map_err(|e| ProviderError::CustomError(format!("Failed to parse function: {e}")))?; + let request: Eip1559TransactionRequest = request + .clone() + .try_into() + .map_err(|e| ProviderError::CustomError(format!("Failed to convert request: {e}")))?; let transaction: TypedTransaction = request.into(); let encoded_output = Middleware::call(self, &transaction, None).await?; diff --git a/src/zks_wallet/mod.rs b/src/zks_wallet/mod.rs index 49eaf57..f408b6c 100644 --- a/src/zks_wallet/mod.rs +++ b/src/zks_wallet/mod.rs @@ -1,5 +1,5 @@ mod errors; -pub use errors::ZKSWalletError; +pub use errors::{ZKRequestError, ZKSWalletError}; mod requests; pub use requests::{ diff --git a/src/zks_wallet/requests/call_request.rs b/src/zks_wallet/requests/call_request.rs index f1ea6e8..2b2f8c6 100644 --- a/src/zks_wallet/requests/call_request.rs +++ b/src/zks_wallet/requests/call_request.rs @@ -1,10 +1,13 @@ use ethers::{ - abi::{encode, Function, HumanReadableParser}, - types::{transaction::eip1559::Eip1559RequestError, Address, Eip1559TransactionRequest}, + abi::{encode, Function, HumanReadableParser, ParseError}, + types::{Address, Eip1559TransactionRequest}, }; use std::fmt::Debug; -use crate::zks_utils::{self, is_precompile}; +use crate::{ + zks_utils::{self, is_precompile}, + zks_wallet::errors::ZKRequestError, +}; #[derive(Clone, Debug)] pub struct CallRequest { @@ -37,29 +40,27 @@ impl CallRequest { self } - pub fn get_parsed_function(&self) -> Function { - let function = if self.to == zks_utils::ECADD_PRECOMPILE_ADDRESS { - zks_utils::ec_add_function() + pub fn get_parsed_function(&self) -> Result { + if self.to == zks_utils::ECADD_PRECOMPILE_ADDRESS { + Ok(zks_utils::ec_add_function()) } else if self.to == zks_utils::ECMUL_PRECOMPILE_ADDRESS { - zks_utils::ec_mul_function() + Ok(zks_utils::ec_mul_function()) } else if self.to == zks_utils::MODEXP_PRECOMPILE_ADDRESS { - zks_utils::mod_exp_function() + Ok(zks_utils::mod_exp_function()) } else { - HumanReadableParser::parse_function(&self.function_signature).unwrap() - }; - function + HumanReadableParser::parse_function(&self.function_signature) + .map_err(ParseError::LexerError) + } } } impl TryFrom for Eip1559TransactionRequest { - type Error = Eip1559RequestError; + type Error = ZKRequestError; fn try_from(request: CallRequest) -> Result { - let function = request.get_parsed_function(); + let function = request.get_parsed_function()?; let function_args = if let Some(function_args) = request.function_parameters { - function - .decode_input(&zks_utils::encode_args(&function, &function_args).unwrap()) - .unwrap() + function.decode_input(&zks_utils::encode_args(&function, &function_args)?)? } else { vec![] }; @@ -68,7 +69,7 @@ impl TryFrom for Eip1559TransactionRequest { // The contract to call is a precompile with arguments. (true, true) => encode(&function_args), // The contract to call is a regular contract with arguments. - (true, false) => function.encode_input(&function_args).unwrap(), + (true, false) => function.encode_input(&function_args)?, // The contract to call is a precompile without arguments. (false, true) => Default::default(), // The contract to call is a regular contract without arguments. diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index ed5ecce..ac0e899 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -17,7 +17,7 @@ use ethers::{ ecdsa::{RecoveryId, Signature as RecoverableSignature}, schnorr::signature::hazmat::PrehashSigner, }, - ContractError, MiddlewareBuilder, SignerMiddleware, + MiddlewareBuilder, SignerMiddleware, }, providers::Middleware, signers::{Signer, Wallet}, @@ -378,7 +378,7 @@ where { let era_provider = self.get_era_provider()?; - let eip712_request: Eip712TransactionRequest = request.clone().try_into().unwrap(); + let eip712_request: Eip712TransactionRequest = request.clone().try_into()?; let response = era_provider .send_transaction_eip712(&self.l2_wallet, eip712_request) From 6d3a8b356c982c1a5f3b7cebaed11b6df331dd34 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 18 Jul 2023 11:50:10 -0300 Subject: [PATCH 17/26] Add request conversion error --- src/zks_wallet/errors.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/zks_wallet/errors.rs b/src/zks_wallet/errors.rs index 449561e..317e456 100644 --- a/src/zks_wallet/errors.rs +++ b/src/zks_wallet/errors.rs @@ -1,4 +1,5 @@ use ethers::{ + abi::{Error, ParseError}, prelude::{ k256::{ ecdsa::{RecoveryId, Signature as RecoverableSignature}, @@ -36,6 +37,8 @@ where NoL2ProviderError(), #[error("Contract error: {0}")] ContractError(#[from] ContractError), + #[error("Contract error: {0}")] + RequestConversionError(#[from] ZKRequestError), #[error("{0}")] CustomError(String), #[error("Main contract error: {0}")] @@ -51,3 +54,15 @@ where Self::CustomError(format!("{value:?}")) } } + +#[derive(thiserror::Error, Debug)] +pub enum ZKRequestError { + #[error("Error parsing function: {0}")] + ParseFunctionError(#[from] ParseError), + #[error("ABI error: {0}")] + AbiError(#[from] AbiError), + #[error("Encoding or decoding error: {0}")] + Error(#[from] Error), + #[error("{0}")] + CustomError(String), +} From aa399f456d585f6a22f13284d2338756e71e572e Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 18 Jul 2023 12:06:06 -0300 Subject: [PATCH 18/26] Fix unused import --- src/zks_wallet/requests/transfer_request.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/zks_wallet/requests/transfer_request.rs b/src/zks_wallet/requests/transfer_request.rs index 67218c9..b28e58e 100644 --- a/src/zks_wallet/requests/transfer_request.rs +++ b/src/zks_wallet/requests/transfer_request.rs @@ -1,8 +1,6 @@ use ethers::types::{Address, Eip1559TransactionRequest, U256}; use std::fmt::Debug; -use crate::zks_utils::ERA_CHAIN_ID; - #[derive(Clone, Debug)] pub struct TransferRequest { pub amount: U256, @@ -31,6 +29,5 @@ impl From for Eip1559TransactionRequest { .to(request.to) .value(request.amount) .from(request.from) - .chain_id(ERA_CHAIN_ID) } } From 760cd5d424b659f3654a483095b40effa90a0a88 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 18 Jul 2023 14:33:35 -0300 Subject: [PATCH 19/26] Update README and payment example --- README.md | 87 +++++++++++++-------------------- examples/simple_payment/main.rs | 61 ++++++----------------- 2 files changed, 48 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 7c79508..3f8f07d 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,23 @@ ## Table of Contents -- [Getting Started with zkSync Web3 SDK](#getting-started-with-zksync-web3-sdk) - - [Prerequisites](#prerequisites) - - [Adding dependencies](#adding-dependencies) - - [First steps](#first-steps) - - [Importing dependencies](#importing-dependencies) - - [Creating a Wallet](#creating-a-wallet) - - [Connecting to the zkSync Network](#connecting-to-the-zksync-network) - - [Creating a Payment Transaction](#creating-a-payment-transaction) - - [Sending the Transaction](#sending-the-transaction) - - [Checking zkSync account balance](#checking-zksync-account-balance) - - [Simple Transfer Example](#simple-transfer-example) - - [Clone the Repository](#clone-the-repository) - - [Run a zkSync localnet](#run-a-zksync-localnet) - - [Run the Simple Transfer Example](#run-the-simple-transfer-example) - - [Conclusion](#conclusion) +- [zksync-web3-rs](#zksync-web3-rs) + - [Table of Contents](#table-of-contents) + - [Getting Started with zkSync Web3 SDK](#getting-started-with-zksync-web3-sdk) + - [Prerequisites](#prerequisites) + - [Adding dependencies](#adding-dependencies) + - [First steps](#first-steps) + - [Importing dependencies](#importing-dependencies) + - [Connecting to the zkSync Network](#connecting-to-the-zksync-network) + - [Creating a ZK-Wallet](#creating-a-zk-wallet) + - [Creating a Payment Transaction](#creating-a-payment-transaction) + - [Sending the Transaction](#sending-the-transaction) + - [Checking zkSync account balance](#checking-zksync-account-balance) + - [Simple Transfer Example](#simple-transfer-example) + - [Clone the Repository](#clone-the-repository) + - [Run a zkSync localnet](#run-a-zksync-localnet) + - [Run the Simple Transfer Example](#run-the-simple-transfer-example) + - [Conclusion](#conclusion) ## Getting Started with zkSync Web3 SDK @@ -56,9 +58,16 @@ Import the `zksync-web3-rs` library into your project by adding the following li use zksync-web3-rs as zksync; ``` -#### Creating a Wallet +#### Connecting to the zkSync Network + +To connect to the zkSync network, you need to provide the URL of the zkSync node. The localnet runs both an *Ethereum* node (L1) on port `8545` and an *Era* node (L2) on port `3050`. You can connect to the zkSync Era network using the following code: + +```rust +let provider = zksync::Provider::try_from("http://localhost:3050").unwrap(); +``` + +#### Creating a ZK-Wallet -To create a wallet, you need to provide the private key of the Ethereum account that will be used to sign the transaction. You can create a wallet using the following code: > We set the chain id to 270 because we are using the zkSync Era node. If you want to use the mainnet, you should set the chain id to 9. > https://era.zksync.io/docs/tools/hardhat/testing.html#connect-wallet-to-local-nodes @@ -70,14 +79,7 @@ let private_key: Wallet = "0x7726827caac94a7f9e1b160f7ea819f172f7b6f let zksync_era_chain_id: u64 = 270; let wallet = zksync::Wallet::with_chain_id(private_key, zksync_era_chain_id); -``` - -#### Connecting to the zkSync Network - -To connect to the zkSync network, you need to provide the URL of the zkSync node. The localnet runs both an *Ethereum* node (L1) on port `8545` and an *Era* node (L2) on port `3050`. You can connect to the zkSync Era network using the following code: - -```rust -let provider = zksync::Provider::try_from("http://localhost:3050").unwrap(); +let zk_wallet = zksync::ZKSWallet::new(wallet, None, Some(provider), None).unwrap(); ``` #### Creating a Payment Transaction @@ -91,41 +93,20 @@ let sender_address: zksync::Address = "0x36615Cf349d7F6344891B1e7CA7C72883F5dc04 let receiver_address: zksync::Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618".parse().unwrap(); let amount_to_transfer = zksync::U256::from(1); -let mut payment_request = zksync::Eip1559TransactionRequest::new() - .from(sender_address) - .to(receiver_address) - .value(amount_to_transfer); - -let fee = provider - .clone() - .estimate_fee(payment_request.clone()) - .await - .unwrap(); - -payment_request = payment_request.max_priority_fee_per_gas(fee.max_priority_fee_per_gas); -payment_request = payment_request.max_fee_per_gas(fee.max_fee_per_gas); - -let transaction: zksync::TypedTransaction = payment_request.into(); +let mut payment_request = zksync::zks_wallet::TransferRequest::with(amount_to_transfer + receiver_address) + .from(sender_address); ``` #### Sending the Transaction -To send the transaction, you need to provide the wallet and the transaction. You can send the transaction using the following code: +To send the payment transaction, you need to provide use the wallet and the transaction. You can send the transaction using the following code: -> In case you are wondering, the transaction is signed in the `send_transaction` method. +> In case you are wondering, the transaction is signed in the `send_transaction` method inside the transfer process. ```rust -use zksync::MiddlewareBuilder; - -let signer_middleware = provider.clone().with_signer(wallet); - let payment_response: zksync::TransactionReceipt = - zksync::SignerMiddleware::send_transaction(&signer_middleware, transaction, None) - .await - .unwrap() - .await - .unwrap() - .unwrap(); + zk_wallet.transfer(payment_request, None).await.unwrap(); ``` #### Checking zkSync account balance @@ -165,7 +146,7 @@ docker-compose up To run the payment transaction example using EIP1559 transactions on zkSync Era, run the following command: ```bash -cargo run --example simple_payment --host --port --amount --from --to --private-key --network +cargo run --example simple_payment -- --host --port --amount --from --to --private-key ``` - `HOST`: The IP address or hostname of the node. diff --git a/examples/simple_payment/main.rs b/examples/simple_payment/main.rs index 07167e9..b608f48 100644 --- a/examples/simple_payment/main.rs +++ b/examples/simple_payment/main.rs @@ -1,19 +1,16 @@ use clap::Parser; use ethers::{ abi::Address, - middleware::SignerMiddleware, - prelude::{k256::ecdsa::SigningKey, MiddlewareBuilder}, + prelude::k256::ecdsa::SigningKey, providers::{Middleware, Provider}, signers::{Signer, Wallet}, - types::{ - transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, TransactionReceipt, U256, - }, + types::{TransactionReceipt, U256}, }; -use zksync_web3_rs::zks_provider::ZKSProvider; +use zksync_web3_rs::{zks_wallet::TransferRequest, ZKSWallet}; // It is set so that the transaction is replay-protected (EIP-155) // https://era.zksync.io/docs/api/hardhat/testing.html#connect-wallet-to-local-nodes -const L1_CHAIN_ID: u64 = 9; +//const L1_CHAIN_ID: u64 = 9; const L2_CHAIN_ID: u64 = 270; #[derive(Parser)] @@ -23,19 +20,13 @@ struct Args { #[clap(short, long)] pub port: u16, #[clap(long, name = "AMOUNT_TO_TRANSFER")] - pub amount: U256, + pub amount: String, #[clap(long, name = "SENDER_ADDRESS")] pub from: Address, #[clap(long, name = "RECEIVER_ADDRESS")] pub to: Address, #[clap(long, name = "SENDER_PRIVATE_KEY")] pub private_key: Wallet, - #[clap( - long, - name = "NETWORK_NAME", - help = "Available networks: \"era\" or \"eth\"" - )] - pub network: String, } #[tokio::main] @@ -48,15 +39,10 @@ async fn main() { let args = Args::parse(); /* Connecting to the node */ + let amount = U256::from_dec_str(&args.amount).unwrap(); + log::debug!("Amount to transfer: {:?}", amount); + let signer = Wallet::with_chain_id(args.private_key, L2_CHAIN_ID); - let signer = Wallet::with_chain_id( - args.private_key, - if args.network == "eth" { - L1_CHAIN_ID - } else { - L2_CHAIN_ID - }, - ); let provider = Provider::try_from(format!( "http://{host}:{port}", host = args.host, @@ -64,33 +50,19 @@ async fn main() { )) .unwrap() .interval(std::time::Duration::from_millis(10)); - let signer_middleware = provider.clone().with_signer(signer); - - /* Payment transaction building */ - let mut payment_request = Eip1559TransactionRequest::new() - .from(args.from) - .to(args.to) - .value(args.amount); + let zk_wallet = ZKSWallet::new(signer, None, Some(provider.clone()), None).unwrap(); - if args.network == "era" { - let fee = provider - .estimate_fee(payment_request.clone()) - .await - .unwrap(); - payment_request = payment_request.max_priority_fee_per_gas(fee.max_priority_fee_per_gas); - payment_request = payment_request.max_fee_per_gas(fee.max_fee_per_gas); - } - - let transaction: TypedTransaction = payment_request.into(); + /* Payment transaction building */ + let payment_request = TransferRequest::with(amount, args.to).from(args.from); - log::debug!("{:?}", transaction); + log::debug!("{:?}", payment_request); /* Sending the payment transaction */ log::debug!( "Sender's balance before paying: {:?}", - provider.get_balance(args.from, None).await.unwrap() + provider.clone().get_balance(args.from, None).await.unwrap() ); log::debug!( "Receiver's balance before getting payed: {:?}", @@ -98,12 +70,7 @@ async fn main() { ); let payment_response: TransactionReceipt = - SignerMiddleware::send_transaction(&signer_middleware, transaction, None) - .await - .unwrap() - .await - .unwrap() - .unwrap(); + zk_wallet.transfer(payment_request, None).await.unwrap(); log::info!("{:?}", payment_response); log::debug!( From f33c543675edfa76e83d05e31cac609bc7e21ea8 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 20 Jul 2023 17:03:49 -0300 Subject: [PATCH 20/26] Return contract address in deploy function --- src/zks_provider/mod.rs | 12 +++--------- src/zks_wallet/wallet.rs | 29 ++++++++++++----------------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 5f819da..e55ca5f 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -1798,9 +1798,7 @@ mod tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["0".to_owned()]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let contract_address = transaction_receipt.contract_address.unwrap(); + let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let call_request = CallRequest::with(contract_address, "getValue()(uint256)".to_owned()); let initial_value = ZKSProvider::call(&era_provider, &call_request) .await @@ -1864,9 +1862,7 @@ mod tests { serde_json::from_reader(File::open(contract_path).unwrap()).unwrap(); let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let contract_address = transaction_receipt.contract_address.unwrap(); + let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let call_request = CallRequest::with(contract_address, "str_out()(string)".to_owned()); let output = ZKSProvider::call(&era_provider, &call_request) .await @@ -1893,9 +1889,7 @@ mod tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let contract_address = transaction_receipt.contract_address.unwrap(); + let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let call_request = CallRequest::with(contract_address, "plus_one(uint256)".to_owned()) .function_parameters(vec!["1".to_owned()]); let no_return_type_output = ZKSProvider::call(&era_provider, &call_request) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index ac0e899..abe99ab 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -369,10 +369,7 @@ where Ok(contract_address) } - pub async fn deploy( - &self, - request: &DeployRequest, - ) -> Result> + pub async fn deploy(&self, request: &DeployRequest) -> Result> where M: ZKSProvider, { @@ -384,11 +381,17 @@ where .send_transaction_eip712(&self.l2_wallet, eip712_request) .await?; - era_provider + let transaction_receipt = era_provider .get_transaction_receipt(response.1) .await? .ok_or(ZKSWalletError::CustomError( "no transaction receipt".to_owned(), + ))?; + + transaction_receipt + .contract_address + .ok_or(ZKSWalletError::CustomError( + "no contract address".to_owned(), )) } @@ -796,9 +799,7 @@ mod zks_signer_tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["10".to_owned()]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let contract_address = transaction_receipt.contract_address.unwrap(); + let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let deploy_result = era_provider.get_code(contract_address, None).await; assert!(deploy_result.is_ok()); @@ -822,9 +823,7 @@ mod zks_signer_tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["Hey".to_owned()]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let contract_address = transaction_receipt.contract_address.unwrap(); + let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let deploy_result = era_provider.get_code(contract_address, None).await; assert!(deploy_result.is_ok()); @@ -849,9 +848,7 @@ mod zks_signer_tests { let deploy_request = DeployRequest::with(counter_contract.abi, counter_contract.bin.to_vec(), vec![]) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let counter_contract_address = transaction_receipt.contract_address.unwrap(); + let counter_contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let deploy_result = era_provider.get_code(counter_contract_address, None).await; assert!(deploy_result.is_ok()); @@ -869,9 +866,7 @@ mod zks_signer_tests { vec![format!("{counter_contract_address:?}")], ) .from(zk_wallet.l2_address()); - let transaction_receipt = zk_wallet.deploy(&deploy_request).await.unwrap(); - - let import_contract_address = transaction_receipt.contract_address.unwrap(); + let import_contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); let call_request = CallRequest::with( import_contract_address, "getCounterValue()(uint256)".to_owned(), From cda5391a914084ca1802cecafb9fa4279d079b70 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Thu, 20 Jul 2023 17:34:27 -0300 Subject: [PATCH 21/26] Remove unnecesary sleeps in tests --- src/zks_wallet/wallet.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index abe99ab..604f5ef 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -596,8 +596,6 @@ mod zks_signer_tests { assert_eq!(receipt.from, zk_wallet.l2_address()); assert_eq!(receipt.to.unwrap(), receiver_address); - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - let sender_balance_after = era_provider .get_balance(zk_wallet.l2_address(), None) .await @@ -755,8 +753,6 @@ mod zks_signer_tests { assert_eq!(receipt.from, zk_wallet.l2_address()); assert_eq!(receipt.to.unwrap(), receiver_address); - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - let sender_balance_after = era_provider .get_balance(zk_wallet.l2_address(), None) .await From d510941f2e93d5437f62e8c1b2c715d0b4d6d6bb Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 25 Jul 2023 17:32:31 -0300 Subject: [PATCH 22/26] Update request builder methods --- src/zks_wallet/requests/call_request.rs | 2 +- src/zks_wallet/requests/transfer_request.rs | 14 ++++++++++++-- src/zks_wallet/requests/withdraw_request.rs | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/zks_wallet/requests/call_request.rs b/src/zks_wallet/requests/call_request.rs index 2b2f8c6..90e5985 100644 --- a/src/zks_wallet/requests/call_request.rs +++ b/src/zks_wallet/requests/call_request.rs @@ -17,7 +17,7 @@ pub struct CallRequest { } impl CallRequest { - pub fn with(to: Address, function_signature: String) -> Self { + pub fn new(to: Address, function_signature: String) -> Self { Self { to, function_signature, diff --git a/src/zks_wallet/requests/transfer_request.rs b/src/zks_wallet/requests/transfer_request.rs index b28e58e..7f8ffe9 100644 --- a/src/zks_wallet/requests/transfer_request.rs +++ b/src/zks_wallet/requests/transfer_request.rs @@ -9,10 +9,10 @@ pub struct TransferRequest { } impl TransferRequest { - pub fn with(amount: U256, to: Address) -> Self { + pub fn new(amount: U256) -> Self { Self { amount, - to, + to: Default::default(), from: Default::default(), } } @@ -21,6 +21,16 @@ impl TransferRequest { self.from = from; self } + + pub fn to(mut self, to: Address) -> Self { + self.to = to; + self + } + + pub fn amount(mut self, amount: U256) -> Self { + self.amount = amount; + self + } } impl From for Eip1559TransactionRequest { diff --git a/src/zks_wallet/requests/withdraw_request.rs b/src/zks_wallet/requests/withdraw_request.rs index b88c552..f126efb 100644 --- a/src/zks_wallet/requests/withdraw_request.rs +++ b/src/zks_wallet/requests/withdraw_request.rs @@ -10,7 +10,7 @@ pub struct WithdrawRequest { } impl WithdrawRequest { - pub fn with(amount: U256) -> Self { + pub fn new(amount: U256) -> Self { Self { amount, to: Default::default(), From 75ec65d305c832faee91ffdd8fe17f3f550c2099 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 25 Jul 2023 17:33:25 -0300 Subject: [PATCH 23/26] Change return type to pending transaction --- src/zks_provider/mod.rs | 88 ++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/src/zks_provider/mod.rs b/src/zks_provider/mod.rs index 00db2a3..2629136 100644 --- a/src/zks_provider/mod.rs +++ b/src/zks_provider/mod.rs @@ -12,8 +12,8 @@ use ethers::{ signers::{Signer, Wallet}, types::{ transaction::{eip2718::TypedTransaction, eip712::Eip712Error}, - Address, BlockNumber, Eip1559TransactionRequest, Signature, TransactionReceipt, H256, U256, - U64, + Address, BlockNumber, Eip1559TransactionRequest, Signature, TransactionReceipt, TxHash, + H256, U256, U64, }, }; use ethers_contract::providers::PendingTransaction; @@ -212,7 +212,7 @@ pub trait ZKSProvider { async fn wait_for_finalize( &self, - transaction_receipt: TransactionReceipt, + transaction_receipt: TxHash, polling_time_in_seconds: Option, timeout_in_seconds: Option, ) -> Result; @@ -223,7 +223,7 @@ pub trait ZKSProvider { &self, wallet: &Wallet, transaction: T, - ) -> Result<(Vec, H256), ProviderError> + ) -> Result, ProviderError> where T: TryInto + Send + Sync + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync; @@ -452,7 +452,7 @@ impl ZKSProvider for SignerMiddleware, transaction: T, - ) -> Result<(Vec, H256), ProviderError> + ) -> Result, ProviderError> where T: TryInto + Sync + Send + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, @@ -464,7 +464,7 @@ impl ZKSProvider for SignerMiddleware, timeout_in_seconds: Option, ) -> Result { @@ -695,7 +695,7 @@ impl ZKSProvider for Provider

{ &self, wallet: &Wallet, transaction: T, - ) -> Result<(Vec, H256), ProviderError> + ) -> Result, ProviderError> where T: TryInto + Sync + Send + Debug, D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync, @@ -727,22 +727,12 @@ impl ZKSProvider for Provider

{ .map_err(|e| ProviderError::CustomError(format!("error signing transaction: {e}")))?; request = request.custom_data(custom_data.custom_signature(signature.to_vec())); - let pending_transaction = self - .send_raw_transaction( - [&[EIP712_TX_TYPE], &*request.rlp_unsigned()] - .concat() - .into(), - ) - .await?; - - let transaction_receipt = pending_transaction - .await? - .ok_or(ProviderError::CustomError( - "no transaction receipt".to_owned(), - ))?; - - // TODO: decode function output. - Ok((Vec::new(), transaction_receipt.transaction_hash)) + self.send_raw_transaction( + [&[EIP712_TX_TYPE], &*request.rlp_unsigned()] + .concat() + .into(), + ) + .await } async fn send_eip712( @@ -844,7 +834,7 @@ impl ZKSProvider for Provider

{ async fn wait_for_finalize( &self, - transaction_receipt: TransactionReceipt, + tx_hash: TxHash, polling_time_in_seconds: Option, timeout_in_seconds: Option, ) -> Result { @@ -852,6 +842,13 @@ impl ZKSProvider for Provider

{ let mut timer = tokio::time::interval(polling_time_in_seconds); let start = Instant::now(); + let transaction_receipt = + self.get_transaction_receipt(tx_hash) + .await? + .ok_or(ProviderError::CustomError( + "No transaction receipt".to_owned(), + ))?; + loop { timer.tick().await; @@ -1332,21 +1329,13 @@ mod tests { #[tokio::test] async fn test_provider_debug_trace_transaction() { let era_provider = era_provider(); - let zk_wallet = ZKSWallet::new(local_wallet(), None, Some(era_signer()), None).unwrap(); + let zk_wallet = + ZKSWallet::new(local_wallet(), None, Some(era_provider.clone()), None).unwrap(); - let transfer_request = TransferRequest::with( - 1_u64.into(), - Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), - ) - .from(zk_wallet.l2_address()); - let transaction_hash = zk_wallet - .transfer(transfer_request, None) - .await - .unwrap() - .await - .unwrap() - .unwrap() - .transaction_hash; + let transfer_request = TransferRequest::new(1_u64.into()) + .to(Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap()) + .from(zk_wallet.l2_address()); + let transaction_hash = zk_wallet.transfer(&transfer_request, None).await.unwrap(); let invalid_transaction_hash: H256 = "0x84472204e445cb3cd5f3ce5e23abcc2892cda5e61b35855a7f0bb1562a6e30e7" .parse() @@ -1730,19 +1719,10 @@ mod tests { let zk_wallet = ZKSWallet::new(local_wallet(), None, Some(era_signer.clone()), None).unwrap(); - let transfer_request = TransferRequest::with( - 1_u64.into(), - Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap(), - ) - .from(zk_wallet.l2_address()); - let transaction_hash = zk_wallet - .transfer(transfer_request, None) - .await - .unwrap() - .await - .unwrap() - .unwrap() - .transaction_hash; + let transfer_request = TransferRequest::new(1_u64.into()) + .to(Address::from_str("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049").unwrap()) + .from(zk_wallet.l2_address()); + let transaction_hash = zk_wallet.transfer(&transfer_request, None).await.unwrap(); let invalid_transaction_hash: H256 = "0x84472204e445cb3cd5f3ce5e23abcc2892cda5e61b35855a7f0bb1562a6e30e7" .parse() @@ -1793,7 +1773,7 @@ mod tests { DeployRequest::with(contract.abi, contract.bin.to_vec(), vec!["0".to_owned()]) .from(zk_wallet.l2_address()); let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); - let call_request = CallRequest::with(contract_address, "getValue()(uint256)".to_owned()); + let call_request = CallRequest::new(contract_address, "getValue()(uint256)".to_owned()); let initial_value = ZKSProvider::call(&era_provider, &call_request) .await .unwrap(); @@ -1863,7 +1843,7 @@ mod tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]); let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); - let call_request = CallRequest::with(contract_address, "str_out()(string)".to_owned()); + let call_request = CallRequest::new(contract_address, "str_out()(string)".to_owned()); let output = ZKSProvider::call(&era_provider, &call_request) .await .unwrap(); @@ -1890,7 +1870,7 @@ mod tests { let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![]) .from(zk_wallet.l2_address()); let contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); - let call_request = CallRequest::with(contract_address, "plus_one(uint256)".to_owned()) + let call_request = CallRequest::new(contract_address, "plus_one(uint256)".to_owned()) .function_parameters(vec!["1".to_owned()]); let no_return_type_output = ZKSProvider::call(&era_provider, &call_request) .await From 97b51259633e08c5b941a089808c03cc44fc075d Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 25 Jul 2023 17:33:49 -0300 Subject: [PATCH 24/26] Return transaction ID in every wallet operation --- src/zks_wallet/wallet.rs | 159 +++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 74 deletions(-) diff --git a/src/zks_wallet/wallet.rs b/src/zks_wallet/wallet.rs index 86e544d..fc97724 100644 --- a/src/zks_wallet/wallet.rs +++ b/src/zks_wallet/wallet.rs @@ -23,10 +23,9 @@ use ethers::{ signers::{Signer, Wallet}, types::{ transaction::eip2718::TypedTransaction, Address, Bytes, Eip1559TransactionRequest, Log, - Signature, TransactionReceipt, H160, H256, U256, + Signature, H160, H256, U256, }, }; -use ethers_contract::providers::PendingTransaction; use serde_json::Value; use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr, sync::Arc}; @@ -152,16 +151,16 @@ where pub async fn transfer( &self, - request: TransferRequest, + request: &TransferRequest, // TODO: Support multiple-token transfers. _token: Option

, - ) -> Result::Provider>, ZKSWalletError> + ) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; - let mut transfer_request: Eip1559TransactionRequest = request.into(); + let mut transfer_request: Eip1559TransactionRequest = request.clone().into(); let fee = era_provider.estimate_fee(transfer_request.clone()).await?; transfer_request = transfer_request.max_priority_fee_per_gas(fee.max_priority_fee_per_gas); @@ -170,8 +169,15 @@ where let transaction: TypedTransaction = transfer_request.into(); // TODO: add block as an override. - let pending_transaction = era_provider.send_transaction(transaction, None).await?; - Ok(pending_transaction) + let transaction_receipt = era_provider + .send_transaction(transaction, None) + .await? + .await? + .ok_or(ZKSWalletError::CustomError( + "No transaction receipt".to_owned(), + ))?; + + Ok(transaction_receipt.transaction_hash) } pub async fn transfer_eip712( @@ -179,30 +185,24 @@ where request: &TransferRequest, // TODO: Support multiple-token transfers. _token: Option
, - ) -> Result::Provider>, ZKSWalletError> + ) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; - let response = era_provider - .send_transaction_eip712(&self.l2_wallet, request.clone()) - .await?; - let transaction_receipt = era_provider - .get_transaction_receipt(response.1) + .send_transaction_eip712(&self.l2_wallet, request.clone()) + .await? .await? .ok_or(ZKSWalletError::CustomError( - "no transaction receipt".to_owned(), + "No transaction receipt".to_owned(), ))?; - Ok(transaction_receipt) + Ok(transaction_receipt.transaction_hash) } - pub async fn deposit( - &self, - request: &DepositRequest, - ) -> Result> + pub async fn deposit(&self, request: &DepositRequest) -> Result> where M: ZKSProvider, { @@ -245,7 +245,7 @@ where ) .await?; - Ok(receipt) + Ok(receipt.transaction_hash) } async fn get_base_cost( @@ -373,43 +373,38 @@ where let eip712_request: Eip712TransactionRequest = request.clone().try_into()?; - let response = era_provider - .send_transaction_eip712(&self.l2_wallet, eip712_request) - .await?; - let transaction_receipt = era_provider - .get_transaction_receipt(response.1) + .send_transaction_eip712(&self.l2_wallet, eip712_request) + .await? .await? .ok_or(ZKSWalletError::CustomError( - "no transaction receipt".to_owned(), + "No transaction receipt".to_owned(), ))?; transaction_receipt .contract_address .ok_or(ZKSWalletError::CustomError( - "no contract address".to_owned(), + "No contract address".to_owned(), )) } - pub async fn withdraw( - &self, - request: &WithdrawRequest, - ) -> Result> + pub async fn withdraw(&self, request: &WithdrawRequest) -> Result> where M: ZKSProvider, { let era_provider = self.get_era_provider()?; - let response = era_provider + let transaction_receipt = era_provider .send_transaction_eip712(&self.l2_wallet, request.clone()) - .await?; + .await? + .await? + .ok_or(ZKSWalletError::CustomError( + "No transaction receipt".to_owned(), + ))?; - response.map_err(|e| ZKSWalletError::CustomError(format!("Error calling withdraw: {e}"))) + Ok(transaction_receipt.transaction_hash) } - pub async fn finalize_withdraw( - &self, - tx_hash: H256, - ) -> Result::Provider>, ZKSWalletError> + pub async fn finalize_withdraw(&self, tx_hash: H256) -> Result> where M: ZKSProvider, { @@ -512,7 +507,7 @@ where ]; let function_signature = "function finalizeEthWithdrawal(uint256 _l2BlockNumber,uint256 _l2MessageIndex,uint16 _l2TxNumberInBlock,bytes calldata _message,bytes32[] calldata _merkleProof) external"; - let response = eth_provider + let transaction_receipt = eth_provider .send( &self.l1_wallet, main_contract, @@ -520,10 +515,13 @@ where Some(parameters.into()), None, ) - .await; - response.map_err(|e| { - ZKSWalletError::CustomError(format!("Error calling finalizeWithdrawal: {e}")) - }) + .await? + .await? + .ok_or(ZKSWalletError::CustomError( + "No transaction receipt".to_owned(), + ))?; + + Ok(transaction_receipt.transaction_hash) } } @@ -572,10 +570,13 @@ mod zks_signer_tests { println!("Sender balance before: {sender_balance_before}"); println!("Receiver balance before: {receiver_balance_before}"); - let receipt = zk_wallet - .transfer(receiver_address, amount_to_transfer, None) - .await - .unwrap() + let request = TransferRequest::new(amount_to_transfer) + .to(receiver_address) + .from(zk_wallet.l2_address()); + let tx_hash = zk_wallet.transfer(&request, None).await.unwrap(); + + let receipt = era_provider + .get_transaction_receipt(tx_hash) .await .unwrap() .unwrap(); @@ -631,7 +632,12 @@ mod zks_signer_tests { println!("L1 balance before: {l1_balance_before}"); println!("L2 balance before: {l2_balance_before}"); - let receipt = zk_wallet.deposit(&request).await.unwrap(); + let tx_hash = zk_wallet.deposit(&request).await.unwrap(); + let receipt = l1_provider + .get_transaction_receipt(tx_hash) + .await + .unwrap() + .unwrap(); assert_eq!(receipt.status.unwrap(), 1_u8.into()); let _l2_receipt = l2_provider @@ -681,7 +687,12 @@ mod zks_signer_tests { println!("L1 balance before: {l1_balance_before}"); println!("L2 balance before: {l2_balance_before}"); - let receipt = zk_wallet.deposit(&request).await.unwrap(); + let tx_hash = zk_wallet.deposit(&request).await.unwrap(); + let receipt = l1_provider + .get_transaction_receipt(tx_hash) + .await + .unwrap() + .unwrap(); assert_eq!(receipt.status.unwrap(), 1_u8.into()); let _l2_receipt = l2_provider @@ -731,15 +742,19 @@ mod zks_signer_tests { println!("Sender balance before: {sender_balance_before}"); println!("Receiver balance before: {receiver_balance_before}"); - let transfer_request = TransferRequest::with(amount_to_transfer, receiver_address); - let receipt = zk_wallet + let transfer_request = TransferRequest::new(amount_to_transfer) + .to(receiver_address) + .from(zk_wallet.l2_address()); + let tx_hash = zk_wallet .transfer_eip712(&transfer_request, None) .await - .unwrap() + .unwrap(); + + let receipt = era_provider + .get_transaction_receipt(tx_hash) .await .unwrap() .unwrap(); - assert_eq!(receipt.from, zk_wallet.l2_address()); assert_eq!(receipt.to.unwrap(), receiver_address); @@ -853,7 +868,7 @@ mod zks_signer_tests { ) .from(zk_wallet.l2_address()); let import_contract_address = zk_wallet.deploy(&deploy_request).await.unwrap(); - let call_request = CallRequest::with( + let call_request = CallRequest::new( import_contract_address, "getCounterValue()(uint256)".to_owned(), ); @@ -883,17 +898,13 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); - let tx_receipt = zk_wallet - .withdraw(amount_to_withdraw, zk_wallet.l1_address()) - .await - .unwrap() - .await - .unwrap() - .unwrap(); + + let withdraw_request = WithdrawRequest::new(amount_to_withdraw).to(zk_wallet.l1_address()); + let tx_hash = zk_wallet.withdraw(&withdraw_request).await.unwrap(); let tx_receipt = zk_wallet .get_era_provider() .unwrap() - .wait_for_finalize(tx_receipt.clone(), None, None) + .wait_for_finalize(tx_hash, None, None) .await .unwrap(); assert_eq!( @@ -919,14 +930,15 @@ mod zks_signer_tests { "Check that L1 balance has not changed" ); + let tx_finalize_hash = zk_wallet.finalize_withdraw(tx_hash).await.unwrap(); + let tx_finalize_receipt = zk_wallet - .finalize_withdraw(tx_receipt.transaction_hash) - .await + .get_eth_provider() .unwrap() + .get_transaction_receipt(tx_finalize_hash) .await .unwrap() .unwrap(); - println!( "L1 Transaction hash: {:?}", tx_finalize_receipt.transaction_hash @@ -994,13 +1006,8 @@ mod zks_signer_tests { // Withdraw let amount_to_withdraw: U256 = parse_units(1_u8, "ether").unwrap().into(); - let tx_receipt = zk_wallet - .withdraw(amount_to_withdraw, zk_wallet.l1_address()) - .await - .unwrap() - .await - .unwrap() - .unwrap(); + let withdraw_request = WithdrawRequest::new(amount_to_withdraw).to(zk_wallet.l1_address()); + let tx_receipt = zk_wallet.withdraw(&withdraw_request).await.unwrap(); let tx_receipt = zk_wallet .get_era_provider() .unwrap() @@ -1030,14 +1037,18 @@ mod zks_signer_tests { "Check that L1 balance has not changed" ); - let tx_finalize_receipt = zk_wallet + let tx_finalize_hash = zk_wallet .finalize_withdraw(tx_receipt.transaction_hash) .await + .unwrap(); + + let tx_finalize_receipt = zk_wallet + .get_eth_provider() .unwrap() + .get_transaction_receipt(tx_finalize_hash) .await .unwrap() .unwrap(); - println!( "L1 Transaction hash: {:?}", tx_finalize_receipt.transaction_hash From 995813a8bd753af8e83204f59302cc4ef172b34e Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Tue, 25 Jul 2023 17:34:04 -0300 Subject: [PATCH 25/26] Update README and payment example --- README.md | 18 +++++++++++------- examples/simple_payment/main.rs | 15 +++++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f8462fc..8d19a18 100644 --- a/README.md +++ b/README.md @@ -92,26 +92,30 @@ use zksync::zks_provider::ZKSProvider; let receiver_address: zksync::Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618".parse().unwrap(); let amount_to_transfer = zksync::U256::from(1); -let mut payment_request = zksync::zks_wallet::TransferRequest::with(amount_to_transfer - receiver_address) - .from(sender_address); +let mut payment_request = zksync::zks_wallet::TransferRequest::new(amount_to_transfer) + .to(receiver_address) + .from(sender_address); // Use zk_wallet.l2_address() method to send it from the wallet address. ``` #### Sending the Transaction -To send the payment transaction, you need to use the wallet and the transfer parameters. You can send the transaction using the following code: +To send the payment transaction, you need to use the wallet and the transfer request. You can send the transaction using the following code: > In case you are wondering, the transaction is signed in the `send_transaction` method inside the transfer process. ```rust -let pending_payment = +let payment_transaction_id = zk_wallet.transfer(payment_request, None).await.unwrap(); ``` -This will send the transaction to the node but the transaction will not be mined until we `await` on it. That will resolve to a `TransactionReceipt` confirming that the transfer went fine. +This will send the transaction to the node and return its ID (hash). To get more information about the transaction we can ask for the `TransactionReceipt` with the following lines: ```rust -let payment_response: zksync::TransactionReceipt = pending_payment.await.unwrap().unwrap(); +let payment_transaction_receipt = provider + .get_transaction_receipt(payment_transaction_id) + .await + .unwrap() + .unwrap(); ``` #### Checking zkSync account balance diff --git a/examples/simple_payment/main.rs b/examples/simple_payment/main.rs index 4b2f91e..dc47dd3 100644 --- a/examples/simple_payment/main.rs +++ b/examples/simple_payment/main.rs @@ -4,7 +4,7 @@ use ethers::{ prelude::k256::ecdsa::SigningKey, providers::{Middleware, Provider}, signers::{Signer, Wallet}, - types::{TransactionReceipt, U256}, + types::U256, }; use zksync_web3_rs::{zks_wallet::TransferRequest, ZKSWallet}; @@ -54,7 +54,7 @@ async fn main() { let zk_wallet = ZKSWallet::new(signer, None, Some(provider.clone()), None).unwrap(); /* Payment transaction building */ - let payment_request = TransferRequest::with(amount, args.to).from(args.from); + let payment_request = TransferRequest::new(amount).to(args.to).from(args.from); log::debug!("{:?}", payment_request); @@ -69,11 +69,14 @@ async fn main() { provider.get_balance(args.to, None).await.unwrap() ); - let pending_payment_transaction = zk_wallet.transfer(payment_request, None).await.unwrap(); + let payment_transaction_id = zk_wallet.transfer(&payment_request, None).await.unwrap(); + let payment_transaction_receipt = provider + .get_transaction_receipt(payment_transaction_id) + .await + .unwrap() + .unwrap(); - /* Waiting for the payment transaction */ - let payment_response: TransactionReceipt = pending_payment_transaction.await.unwrap().unwrap(); - log::info!("{:?}", payment_response); + log::info!("{:?}", payment_transaction_receipt); log::debug!( "Sender's balance after paying: {:?}", From 2e3082626d8e94de84cee277a1bae8cbd693cea6 Mon Sep 17 00:00:00 2001 From: IAvecilla Date: Mon, 28 Aug 2023 13:36:11 -0300 Subject: [PATCH 26/26] Empty commit