-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
427 additions
and
136 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |