Skip to content

Commit

Permalink
integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
zsluedem committed Dec 22, 2022
1 parent b4ed6c5 commit 4e441fb
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 136 deletions.
269 changes: 135 additions & 134 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ clap = { version = "4", features = ["derive"] }
dirs = "4.0"
educe = { version = "0.4", features = ["Debug", "Default"] }
ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces" }
ethers = "1.0.0"
ethers = {version = "1.0.0", features = ["ethers-solc"]}
expanded-pathbuf = "0.1"
hex = { version = "0.4.3", default-features = false, features = ["std"] }
jsonrpsee = { version = "0.16", features = ["server", "macros"] }
Expand All @@ -30,6 +30,7 @@ tonic = { version = "0.8", default-features = false, features = [
tracing = "0.1"
tracing-subscriber = "0.3"
regex = "1"
eyre = "0.6.8"

[build-dependencies]
anyhow = "1"
Expand All @@ -53,3 +54,7 @@ name = "bundler-rpc"
[[bin]]
path = "bin/create-wallet.rs"
name = "create-wallet"

[[bin]]
path = "bin/compile_contracts.rs"
name = "compile-contracts"
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ cargo-fmt:
cargo fmt --all

cargo-test:
make fetch-thirdparty
make compile-thirdparty
cargo test

fetch-thirdparty:
git submodule update --init
git submodule update --init && cd thirdparty/account-abstraction && yarn install && cd ../..

compile-thirdparty:
cargo run --bin compile-contracts

clean:
cd thirdparty/account-abstraction && yarn clean && cd ../..
cargo clean
6 changes: 6 additions & 0 deletions bin/compile_contracts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use aa_bundler::contracts::compile_account_abstraction;

fn main() -> anyhow::Result<()> {
compile_account_abstraction()?;
Ok(())
}
17 changes: 17 additions & 0 deletions src/contracts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
mod entrypoint;
mod gen;

use std::path::PathBuf;

pub use entrypoint::*;
use ethers::solc::{Project, ProjectCompileOutput, ProjectPathsConfig};

// It would skip compiling if cached arctifacts are found
pub fn compile_account_abstraction() -> anyhow::Result<ProjectCompileOutput> {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("thirdparty/account-abstraction");
let build_path_config = ProjectPathsConfig::hardhat(root)?;
let project = Project::builder().paths(build_path_config).build()?;
let compiled = project.compile()?;
assert!(
!compiled.has_compiler_errors(),
"Compile with errore:\n{:?}",
compiled.output().errors
);
Ok(compiled)
}
10 changes: 10 additions & 0 deletions tests/common/gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use ethers::prelude::abigen;

abigen!(SimpleAccountFactory, "$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/SimpleAccountFactory.sol/SimpleAccountFactory.json");

abigen!(SimpleAccount, "$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/SimpleAccount.sol/SimpleAccount.json");

abigen!(
EntryPointContract,
"$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/EntryPoint.sol/EntryPoint.json"
);
90 changes: 90 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use std::{path::PathBuf, sync::Arc};

use aa_bundler::contracts::compile_account_abstraction;
use ethers::{
abi::Abi,
prelude::ContractFactory,
providers::Middleware,
solc::{Artifact, Project, ProjectCompileOutput, ProjectPathsConfig},
types::{Address, Bytes},
};
pub mod gen;

pub const ANVIL_TEST_KEY_PHRASE: &'static str =
"test test test test test test test test test test test junk";

pub struct CompiledContract {
abi: Abi,
bytecode: Bytes,
runtime_bytecode: Bytes,
}

pub struct AACompiledContracts {
entry_point: CompiledContract,
simple_account_factory: CompiledContract,
}
impl AACompiledContracts {
pub async fn deploy<M>(self, client: Arc<M>) -> anyhow::Result<(Address, Address)>
where
M: Middleware + 'static,
{
let AACompiledContracts {
entry_point,
simple_account_factory,
} = self;
let entry_point_contract =
ContractFactory::new(entry_point.abi, entry_point.bytecode, client.clone())
.deploy(())?
.send()
.await?;

let simple_account_factory = ContractFactory::new(
simple_account_factory.abi,
simple_account_factory.bytecode,
client,
)
.deploy(entry_point_contract.address())?
.send()
.await?;
Ok((
entry_point_contract.address(),
simple_account_factory.address(),
))
}
}

pub fn compile_project() -> anyhow::Result<AACompiledContracts> {
let compiled = compile_account_abstraction()?;

let entry_point = compiled
.find_first("EntryPoint")
.expect("could not find contract");
let (abi, bytecode, runtime_bytecode) = entry_point
.clone()
.into_compact_contract()
.into_parts_or_default();

let entry_point_contract = CompiledContract {
abi,
bytecode,
runtime_bytecode,
};

let simple_account_factory = compiled
.find_first("SimpleAccountFactory")
.expect("could not find contract");
let (abi, bytecode, runtime_bytecode) = simple_account_factory
.clone()
.into_compact_contract()
.into_parts_or_default();
let simple_account_factory = CompiledContract {
abi,
bytecode,
runtime_bytecode,
};

Ok(AACompiledContracts {
entry_point: entry_point_contract,
simple_account_factory: simple_account_factory,
})
}
153 changes: 153 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
pub mod common;

use aa_bundler::contracts::EntryPoint;
use aa_bundler::types::user_operation::UserOperation;
use anyhow::Ok;
use ethers::prelude::k256::ecdsa::SigningKey;
use ethers::providers::Middleware;
use ethers::signers::Wallet;
use ethers::types::TransactionRequest;
use ethers::utils::WEI_IN_ETHER;
use ethers::{
abi::Address,
core::utils::Anvil,
prelude::SignerMiddleware,
providers::{Http, Provider},
signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer},
types::{Bytes, U256},
};
use std::{convert::TryFrom, sync::Arc, time::Duration};

use crate::common::gen::SimpleAccountFactory;
use crate::common::{compile_project, ANVIL_TEST_KEY_PHRASE};

struct TestContest {
pub entry_point_address: Address,
pub simple_account_factory_address: Address,
pub owner_wallet: LocalWallet,
pub simple_account_address: Address,
}

pub async fn sign(
user_op: &mut UserOperation,
entry_point_address: Address,
chain_id: U256,
key: Wallet<SigningKey>,
) -> anyhow::Result<()> {
let user_op_hash = user_op.hash(entry_point_address, chain_id);
let signature = key.sign_message(user_op_hash.as_bytes()).await?;
user_op.signature = Bytes::from(signature.to_vec());
Ok(())
}

async fn deploy_initial_contracts(
endpoint: String,
owner_wallet: LocalWallet,
) -> anyhow::Result<TestContest> {
let provider =
Arc::new(Provider::<Http>::try_from(endpoint)?.interval(Duration::from_millis(10u64)));
let chain_id = provider.clone().get_chainid().await?;
let client = SignerMiddleware::new(
provider,
owner_wallet.clone().with_chain_id(chain_id.as_u64()),
);
let client = Arc::new(client);
let cimpiled_contracts = compile_project()?;

// 1. deploy the entrypoint contract and the simple account factory contract
let (entry_point_address, simple_account_factory_address) =
cimpiled_contracts.deploy(client.clone()).await?;

let simple_account_factory =
SimpleAccountFactory::new(simple_account_factory_address, client.clone());

// 2. create a simple account based on the simple account factory contract
let tx = simple_account_factory.create_account(owner_wallet.address(), U256::from(1));
let simple_account_address = tx.clone().await?;
let res = tx.send().await?;

// 3. send some initial money to the simple account address and make sure it could prepay
let tx = TransactionRequest::new()
.to(simple_account_address)
.value(U256::from(10) * WEI_IN_ETHER)
.from(owner_wallet.address());
client.clone().send_transaction(tx, None).await?.await?;
let receipt = client
.clone()
.get_transaction_receipt(res.tx_hash())
.await?;
println!(
"result: {:?}\n {:?}\n{:?}",
receipt, simple_account_factory_address, entry_point_address
);
Ok(TestContest {
entry_point_address,
simple_account_factory_address,
owner_wallet,
simple_account_address,
})
}

#[tokio::test]
async fn interact_local() -> anyhow::Result<()> {
let wallet2: LocalWallet = MnemonicBuilder::<English>::default()
.phrase(ANVIL_TEST_KEY_PHRASE)
.build()
.unwrap()
.into();
deploy_initial_contracts("http://127.0.0.1:8545".to_string(), wallet2).await?;
Ok(())
}

#[tokio::test]
async fn interact_with_local() -> anyhow::Result<()> {
use aa_bundler::types::user_operation::UserOperation;
use std::convert::From;
let anvil = Anvil::new().spawn();

let context =
deploy_initial_contracts(anvil.endpoint(), anvil.keys()[0].clone().into()).await?;
let provider = Provider::try_from(anvil.endpoint())?.interval(Duration::from_millis(10u64));
let wallet: LocalWallet = anvil.keys()[0].clone().into();
let wallet2: LocalWallet = MnemonicBuilder::<English>::default()
.phrase(ANVIL_TEST_KEY_PHRASE)
.build()
.unwrap()
.into();
println!("signer : {:?}", wallet.address());
println!("pk: {:?}", wallet2.address());
let chain_id = provider.get_chainid().await?;

let client = SignerMiddleware::new(provider, wallet.clone().with_chain_id(chain_id.as_u64()));
let client = Arc::new(client);
let chain_id = client.clone().get_chainid().await?;
println!(
"Currently working on chain id: {}, {:?}\n Entrypoint: {:?}",
chain_id,
wallet.address(),
context.entry_point_address
);
let entry_point_contract = EntryPoint::new(client.clone(), context.entry_point_address.clone());
let mut user_op = UserOperation {
sender: context.simple_account_address,
nonce: U256::zero(),
init_code: Bytes::default(),
call_data: Bytes::default(),
call_gas_limit: U256::from(200000),
verification_gas_limit: U256::from(100000),
pre_verification_gas: U256::from(21000),
max_fee_per_gas: U256::from(3000000000u64),
max_priority_fee_per_gas: U256::from(1000000000),
paymaster_and_data: Bytes::default(),
signature: Bytes::default(),
};

user_op.pack_for_signature();
sign(&mut user_op, context.entry_point_address, chain_id, wallet).await?;
let hash = entry_point_contract
.simulate_validation(user_op)
.await
.expect("error");
println!("{:?}", hash);
Ok(())
}

0 comments on commit 4e441fb

Please sign in to comment.