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

Add create2 feature for contract deployment #125

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ env_logger = "0.10"
# Hash
sha2 = "0.9.5"

[dev-dependencies]
rand = "0.8.5"

[features]
ethers-solc = ["ethers/ethers-solc"]

Expand Down
7 changes: 3 additions & 4 deletions src/eip712/transaction_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,8 @@ impl TryFrom<DeployRequest> for Eip712TransactionRequest {
))
})?,
))?;
let create = contract_deployer.function("create")?;
let create_function = contract_deployer.function(&request.deploy_type.to_string())?;

// 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:?}"))
})?;
Expand All @@ -322,7 +320,8 @@ impl TryFrom<DeployRequest> for Eip712TransactionRequest {
}
};

let data = encode_function_data(create, (salt, bytecode_hash, call_data))?;
let salt = request.salt.unwrap_or([0_u8; 32]);
let data = encode_function_data(create_function, (salt, bytecode_hash, call_data))?;

let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).map_err(|e| {
ZKRequestError::CustomError(format!("Error getting contract deployer address {e:?}"))
Expand Down
68 changes: 56 additions & 12 deletions src/tests/wallet_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ mod zks_signer_tests {
use crate::zks_provider::ZKSProvider;
use crate::zks_utils::{ERA_CHAIN_ID, ETH_CHAIN_ID};
use crate::zks_wallet::{
CallRequest, DeployRequest, DepositRequest, TransferRequest, WithdrawRequest, ZKSWallet,
CallRequest, DepositRequest, TransferRequest, WithdrawRequest, ZKSWallet,
{DeployRequest, DeployType},
};
use ethers::abi::Tokenize;
use ethers::contract::abigen;
use ethers::providers::Middleware;
use ethers::signers::{LocalWallet, Signer};
use ethers::types::Address;
use ethers::types::U256;
use ethers::types::{Address, Bytes};
use ethers::utils::parse_units;
use rand::RngCore;
use std::fs::File;
use std::path::PathBuf;
use std::str::FromStr;
Expand All @@ -27,7 +29,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_transfer() {
let sender_private_key =
"0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let receiver_address: Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618"
.parse()
.unwrap();
Expand Down Expand Up @@ -91,7 +93,7 @@ mod zks_signer_tests {

#[tokio::test]
async fn test_deposit() {
let private_key = "0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
let private_key = "0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let request = DepositRequest::new(parse_units("0.01", "ether").unwrap().into());
println!("Amount: {}", request.amount);

Expand Down Expand Up @@ -143,7 +145,7 @@ mod zks_signer_tests {

#[tokio::test]
async fn test_deposit_to_another_address() {
let private_key = "0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
let private_key = "0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let to: Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618"
.parse()
.unwrap();
Expand Down Expand Up @@ -200,7 +202,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_deposit_erc20_token() {
let amount: U256 = 1_i32.into();
let private_key = "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
let private_key = "0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let l1_provider = eth_provider();
let l2_provider = era_provider();
let wallet = LocalWallet::from_str(private_key).unwrap();
Expand Down Expand Up @@ -249,7 +251,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_transfer_eip712() {
let sender_private_key =
"0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let receiver_address: Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618"
.parse()
.unwrap();
Expand Down Expand Up @@ -313,10 +315,52 @@ mod zks_signer_tests {
);
}

#[tokio::test]
async fn test_deploy_contract_with_create2() {
let deployer_private_key =
"e131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let era_provider = era_provider();
let wallet = LocalWallet::from_str(deployer_private_key)
.unwrap()
.with_chain_id(ERA_CHAIN_ID);
let zk_wallet = ZKSWallet::new(wallet, None, Some(era_provider.clone()), None).unwrap();

let mut contract_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
contract_path.push("src/abi/test_contracts/counter_combined.json");
let contract: CompiledContract =
serde_json::from_reader(File::open(contract_path).unwrap()).unwrap();
let mut salt = [0_u8; 32];
rand::thread_rng().fill_bytes(&mut salt[..]);
let deploy_request = DeployRequest::with(contract.abi, contract.bin.to_vec(), vec![])
.from(zk_wallet.l2_address())
.deploy_type(DeployType::Create2)
.salt(salt);
let first_deploy_contract_address = zk_wallet.deploy(&deploy_request).await.unwrap();
let deploy_result = era_provider
.get_code(first_deploy_contract_address, None)
.await;
assert!(
deploy_result.is_ok(),
"Contract should be deployed successfully the first time"
);

rand::thread_rng().fill_bytes(&mut salt[..]);
let second_deploy_request = deploy_request.salt(salt);
let second_deploy_contract_address =
zk_wallet.deploy(&second_deploy_request).await.unwrap();
let deploy_result: Result<Bytes, ethers::providers::ProviderError> = era_provider
.get_code(second_deploy_contract_address, None)
.await;
assert!(
deploy_result.is_ok(),
"Contract should be deployed successfully the second time"
);
}

#[tokio::test]
async fn test_deploy_contract_with_constructor_arg_uint() {
let deployer_private_key =
"7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let era_provider = era_provider();
let wallet = LocalWallet::from_str(deployer_private_key)
.unwrap()
Expand All @@ -340,7 +384,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_deploy_contract_with_constructor_arg_string() {
let deployer_private_key =
"7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let era_provider = era_provider();
let wallet = LocalWallet::from_str(deployer_private_key)
.unwrap()
Expand All @@ -364,7 +408,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_deploy_contract_with_import() {
let deployer_private_key =
"7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let era_provider = era_provider();
let wallet = LocalWallet::from_str(deployer_private_key)
.unwrap()
Expand Down Expand Up @@ -413,7 +457,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_withdraw_to_same_address() {
let sender_private_key =
"0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let wallet = LocalWallet::from_str(sender_private_key)
.unwrap()
.with_chain_id(ERA_CHAIN_ID);
Expand Down Expand Up @@ -510,7 +554,7 @@ mod zks_signer_tests {
#[tokio::test]
async fn test_withdraw_to_other_address() {
let sender_private_key =
"0x28a574ab2de8a00364d5dd4b07c4f2f574ef7fcc2a86a197f65abaec836d1959";
"0xe131bc3f481277a8f73d680d9ba404cc6f959e64296e0914dded403030d4f705";
let receiver_private_key =
"0xe667e57a9b8aaa6709e51ff7d093f1c5b73b63f9987e4ab4aa9a5c699e024ee8";
let l2_wallet = LocalWallet::from_str(sender_private_key)
Expand Down
7 changes: 5 additions & 2 deletions src/zks_wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ pub use errors::{ZKRequestError, ZKSWalletError};

mod requests;
pub use requests::{
call_request::CallRequest, deploy_request::DeployRequest, deposit_request::DepositRequest,
transfer_request::TransferRequest, withdraw_request::WithdrawRequest,
call_request::CallRequest,
deploy_request::{DeployRequest, DeployType},
deposit_request::DepositRequest,
transfer_request::TransferRequest,
withdraw_request::WithdrawRequest,
};

mod wallet;
Expand Down
31 changes: 30 additions & 1 deletion src/zks_wallet/requests/deploy_request.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
use ethers::{abi::Abi, types::Address};
use std::fmt::Debug;
use std::{fmt::Debug, fmt::Display};

#[derive(Clone, Debug)]
pub enum DeployType {
Create,
Create2,
}

impl Display for DeployType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeployType::Create => write!(f, "create"),
DeployType::Create2 => write!(f, "create2"),
}
}
}

#[derive(Clone, Debug)]
pub struct DeployRequest {
Expand All @@ -8,6 +23,8 @@ pub struct DeployRequest {
pub constructor_parameters: Vec<String>,
pub from: Address,
pub factory_deps: Option<Vec<Vec<u8>>>,
pub deploy_type: DeployType,
pub salt: Option<[u8; 32]>,
}

impl DeployRequest {
Expand All @@ -22,6 +39,8 @@ impl DeployRequest {
constructor_parameters,
from: Default::default(),
factory_deps: None,
deploy_type: DeployType::Create,
salt: None,
}
}

Expand All @@ -34,4 +53,14 @@ impl DeployRequest {
self.factory_deps = Some(factory_deps);
self
}

pub fn deploy_type(mut self, deploy_type: DeployType) -> Self {
self.deploy_type = deploy_type;
self
}

pub fn salt(mut self, salt: [u8; 32]) -> Self {
self.salt = Some(salt);
self
}
}
Loading