Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deploy eip712 #136

Merged
merged 10 commits into from
Sep 20, 2024
557 changes: 305 additions & 252 deletions Cargo.lock

Large diffs are not rendered by default.

411 changes: 411 additions & 0 deletions abi/ContractDeployer.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly-2024-09-06"
2 changes: 1 addition & 1 deletion src/deposit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ where
// overrides.
if allowance < amount {
erc20
.approve(l1_shared_bridge_address, amount * 2)
.approve(l1_shared_bridge_address, amount * 2_u8)
.send()
.await
.unwrap()
Expand Down
2 changes: 2 additions & 0 deletions src/eip712/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub use transaction::Eip712Transaction;
mod paymaster_params;
pub use paymaster_params::PaymasterParams;

pub use transaction_request::DeployRequest;

/// The 32-byte hash of the bytecode of a zkSync contract is calculated in the following way:
///
/// * The first 2 bytes denote the version of bytecode hash format and are currently equal to [1,0].
Expand Down
170 changes: 115 additions & 55 deletions src/eip712/transaction_request.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use super::{rlp_append_option, Eip712Meta};
use crate::{types::L1TxOverrides, utils::MAX_PRIORITY_FEE_PER_GAS};
use super::{hash_bytecode, rlp_append_option, Eip712Meta};
use crate::{
types::L1TxOverrides,
utils::{self, MAX_PRIORITY_FEE_PER_GAS},
};
use ethers::{
abi::{Abi, Error, ParseError},
types::{
transaction::{eip2930::AccessList, eip712::Eip712Error},
Address, Bytes, Signature, U256,
},
utils::rlp::{Encodable, RlpStream},
};
use ethers_contract::{abigen, encode_function_data, AbiError};
use serde::{Deserialize, Serialize};
use zksync_types::{DEFAULT_ERA_CHAIN_ID, EIP_712_TX_TYPE};
use zksync_types::{CONTRACT_DEPLOYER_ADDRESS, DEFAULT_ERA_CHAIN_ID, EIP_712_TX_TYPE};

// TODO: Not all the fields are optional. This was copied from the JS implementation.
#[derive(Serialize, Deserialize, Clone, Debug)]
Expand Down Expand Up @@ -245,57 +250,112 @@ impl Default for Eip712TransactionRequest {
// }
// }

// impl TryFrom<DeployRequest> for Eip712TransactionRequest {
// type Error = ZKRequestError;
use std::{fmt::Debug, fs::File, io::BufReader, path::PathBuf};

// fn try_from(request: DeployRequest) -> Result<Self, Self::Error> {
// 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).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).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(ZKRequestError::CustomError(
// "Constructor not present".to_owned(),
// ))
// }
// (None, true) | (Some(_), true) => Bytes::default(),
// (Some(constructor), false) => {
// utils::encode_constructor_args(constructor, &request.constructor_parameters)?.into()
// }
// };

// let data = encode_function_data(create, (salt, bytecode_hash, call_data))?;
#[derive(Clone, Debug)]
pub struct DeployRequest {
pub contract_abi: Abi,
pub contract_bytecode: Vec<u8>,
pub constructor_parameters: Vec<String>,
pub from: Address,
pub factory_deps: Option<Vec<Vec<u8>>>,
}

// Ok(Eip712TransactionRequest::new()
// .r#type(EIP_712_TX_TYPE)
// .to(CONTRACT_DEPLOYER_ADDRESS)
// .custom_data(custom_data)
// .data(data))
// }
// }
impl DeployRequest {
pub fn with(
contract_abi: Abi,
contract_bytecode: Vec<u8>,
constructor_parameters: Vec<String>,
) -> 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<Vec<u8>>) -> Self {
self.factory_deps = Some(factory_deps);
self
}
}

#[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),
}

abigen!(ContractDeployer, "abi/ContractDeployer.json");

impl TryFrom<DeployRequest> for Eip712TransactionRequest {
type Error = ZKRequestError;

fn try_from(request: DeployRequest) -> Result<Self, Self::Error> {
let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
contract_deployer_path.push("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
})
// TODO CHECK THIS GAS_PER_PUBDATA
// IT IS NEEDED TO COMPLETE THE TX, but is not set dynamically
.gas_per_pubdata(U256::from(50000_u32));

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).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(ZKRequestError::CustomError(
"Constructor not present".to_owned(),
))
}
(None, true) | (Some(_), true) => Bytes::default(),
(Some(constructor), false) => {
utils::encode_constructor_args(constructor, &request.constructor_parameters)?.into()
}
};

let data = encode_function_data(create, (salt, bytecode_hash, call_data))?;

Ok(Eip712TransactionRequest::new()
.r#type(EIP_712_TX_TYPE)
.to(CONTRACT_DEPLOYER_ADDRESS)
.custom_data(custom_data)
.data(data))
}
}
93 changes: 1 addition & 92 deletions src/zk_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use ethers::{
};

use crate::{
eip712::{Eip712Meta, Eip712Transaction, Eip712TransactionRequest},
eip712::{Eip712Transaction, Eip712TransactionRequest},
types::L1TxOverrides,
utils,
};
Expand Down Expand Up @@ -220,17 +220,6 @@ pub trait ZKMiddleware {
tx: Bytes,
) -> Result<TransactionDetailedResult, Self::Error>;

async fn send_eip712<D>(
&self,
wallet: &Wallet<D>,
contract_address: Address,
function_signature: &str,
function_parameters: Option<Vec<String>>,
overrides: Option<L1TxOverrides>,
) -> Result<PendingTransaction<Self::Provider>, Self::Error>
where
D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync;

async fn send<D>(
&self,
wallet: &Wallet<D>,
Expand Down Expand Up @@ -631,86 +620,6 @@ where
.await
}

async fn send_eip712<D>(
&self,
wallet: &Wallet<D>,
contract_address: Address,
function_signature: &str,
function_parameters: Option<Vec<String>>,
overrides: Option<L1TxOverrides>,
) -> Result<PendingTransaction<Self::Provider>, Self::Error>
where
D: PrehashSigner<(RecoverableSignature, RecoveryId)> + Send + Sync,
{
// Note: We couldn't implement Self::Error::LexerError because ethers-rs's LexerError is not exposed.
// TODO check for ECADD precompile address to get the function signature.
let function = HumanReadableParser::parse_function(function_signature)
.map_err(|e| ProviderError::CustomError(e.to_string()))
.map_err(M::convert_err)?;

let mut send_request = if let Some(overrides) = overrides {
Eip712TransactionRequest::from_overrides(overrides)
} else {
Eip712TransactionRequest::new()
};

let function_args = if let Some(function_args) = function_parameters {
function
.decode_input(
&utils::encode_args(&function, &function_args)
.map_err(|e| ProviderError::CustomError(e.to_string()))
.map_err(M::convert_err)?,
)
.map_err(|e| ProviderError::CustomError(e.to_string()))
.map_err(M::convert_err)?
} else {
vec![]
};

send_request = send_request
.r#type(EIP_712_TX_TYPE)
.from(wallet.address())
.to(contract_address)
.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?)
.data(if !function_args.is_empty() {
function
.encode_input(&function_args)
.map_err(|e| ProviderError::CustomError(e.to_string()))
.map_err(M::convert_err)?
} else {
function.short_signature().into()
});

let fee = self.estimate_fee(send_request.clone()).await?;
send_request = send_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 = send_request
.clone()
.try_into()
.map_err(|e: Eip712Error| ProviderError::CustomError(e.to_string()))
.map_err(M::convert_err)?;
let signature: Signature = wallet
.sign_typed_data(&signable_data)
.await
.map_err(|e| ProviderError::CustomError(format!("error signing transaction: {e}")))
.map_err(M::convert_err)?;
send_request =
send_request.custom_data(Eip712Meta::new().custom_signature(signature.to_vec()));

let encoded_rlp = &*send_request
.rlp_signed(signature)
.map_err(|e| ProviderError::CustomError(format!("error encoding transaction: {e}")))
.map_err(M::convert_err)?;
self.send_raw_transaction([&[EIP_712_TX_TYPE], encoded_rlp].concat().into())
.await
}

async fn send<D>(
&self,
wallet: &Wallet<D>,
Expand Down
Loading
Loading