-
Notifications
You must be signed in to change notification settings - Fork 0
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
chore: Safe boilerplate & simple account automated tests #7
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
2f6e185
feat: Safe
chris13524 60c83b3
chore: remove old code
chris13524 7330e52
chore: more Safe stuff
chris13524 6b600e7
chore: test simple account on local
chris13524 7f9d2c2
chore: run infra forked
chris13524 7e832ca
fix: docker service dependencies
chris13524 9a7363c
fix: other contract builds
chris13524 7330acb
chore: install pnpm
chris13524 dc7e926
fix: docker compose cmd
chris13524 9030aca
fix: build path
chris13524 09db5cd
chore: fix health endpoints
chris13524 0947c8e
chore: fix Swift build
chris13524 5f4b4e9
chore: remove use of mnemonic
chris13524 6317995
chore: move `mod.rs` to `safe.rs`, fix warning
jackpooleywc ad768f1
Merge pull request #8 from WalletConnect/jack/safemodule
jackpooleywc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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 |
---|---|---|
@@ -1,3 +1,9 @@ | ||
[submodule "crates/yttrium/src/contracts"] | ||
path = crates/yttrium/src/contracts | ||
url = https://github.com/eth-infinitism/account-abstraction.git | ||
[submodule "crates/yttrium/safe-smart-account"] | ||
path = crates/yttrium/safe-smart-account | ||
url = https://github.com/safe-global/safe-smart-account | ||
[submodule "crates/yttrium/safe-modules"] | ||
path = crates/yttrium/safe-modules | ||
url = https://github.com/safe-global/safe-modules |
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
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,107 @@ | ||
use { | ||
// serde_json::Value, | ||
std::process::{Command, Stdio}, | ||
}; | ||
|
||
fn main() { | ||
build_contracts(); | ||
} | ||
|
||
const CONTRACTS_DIR: &str = "crates/yttrium/safe-smart-account/contracts"; | ||
|
||
fn build_contracts() { | ||
println!("cargo::rerun-if-changed={CONTRACTS_DIR}"); | ||
install_foundry(); | ||
compile_contracts(&format!("{CONTRACTS_DIR}/proxies")); | ||
// extract_bytecodes(); | ||
} | ||
|
||
fn format_foundry_dir(path: &str) -> String { | ||
format!( | ||
"{}/../../../../.foundry/{}", | ||
std::env::var("OUT_DIR").unwrap(), | ||
path | ||
) | ||
} | ||
|
||
fn install_foundry() { | ||
let bin_finished_flag = format_foundry_dir("bin/.finished"); | ||
if std::fs::metadata(&bin_finished_flag).is_ok() { | ||
return; | ||
} | ||
|
||
let bin_folder = format_foundry_dir("bin"); | ||
std::fs::remove_dir_all(&bin_folder).ok(); | ||
std::fs::create_dir_all(&bin_folder).unwrap(); | ||
let output = Command::new("bash") | ||
.args(["-c", &format!("curl https://raw.githubusercontent.com/foundry-rs/foundry/e0ea59cae26d945445d9cf21fdf22f4a18ac5bb2/foundryup/foundryup | FOUNDRY_DIR={} bash", format_foundry_dir(""))]) | ||
.stdout(Stdio::piped()) | ||
.stderr(Stdio::piped()) | ||
.spawn() | ||
.unwrap() | ||
.wait_with_output() | ||
.unwrap(); | ||
println!("foundryup status: {:?}", output.status); | ||
let stdout = String::from_utf8(output.stdout).unwrap(); | ||
println!("foundryup stdout: {stdout:?}"); | ||
let stderr = String::from_utf8(output.stderr).unwrap(); | ||
println!("foundryup stderr: {stderr:?}"); | ||
assert!(output.status.success()); | ||
|
||
std::fs::write(bin_finished_flag, "").unwrap(); | ||
} | ||
|
||
fn compile_contracts(contracts_dir: &str) { | ||
let output = Command::new(format_foundry_dir("bin/forge")) | ||
.args([ | ||
"build", | ||
&format!("--contracts={contracts_dir}"), | ||
"--skip=test", | ||
"--cache-path", | ||
&format_foundry_dir("forge/cache"), | ||
"--out", | ||
&format_foundry_dir("forge/out"), | ||
]) | ||
.stdout(Stdio::piped()) | ||
.stderr(Stdio::piped()) | ||
.spawn() | ||
.unwrap() | ||
.wait_with_output() | ||
.unwrap(); | ||
println!("forge status: {:?}", output.status); | ||
let stdout = String::from_utf8(output.stdout).unwrap(); | ||
println!("forge stdout: {stdout:?}"); | ||
let stderr = String::from_utf8(output.stderr).unwrap(); | ||
println!("forge stderr: {stderr:?}"); | ||
assert!(output.status.success()); | ||
} | ||
|
||
// const ERC6492_FILE: &str = "forge/out/Erc6492.sol/ValidateSigOffchain.json"; | ||
// const ERC6492_BYTECODE_FILE: &str = "forge/out/Erc6492.sol/ValidateSigOffchain.bytecode"; | ||
// const ERC1271_MOCK_FILE: &str = "forge/out/Erc1271Mock.sol/Erc1271Mock.json"; | ||
// const ERC1271_MOCK_BYTECODE_FILE: &str = "forge/out/Erc1271Mock.sol/Erc1271Mock.bytecode"; | ||
// fn extract_bytecodes() { | ||
// extract_bytecode( | ||
// &format_foundry_dir(ERC6492_FILE), | ||
// &format_foundry_dir(ERC6492_BYTECODE_FILE), | ||
// ); | ||
// extract_bytecode( | ||
// &format_foundry_dir(ERC1271_MOCK_FILE), | ||
// &format_foundry_dir(ERC1271_MOCK_BYTECODE_FILE), | ||
// ); | ||
// } | ||
|
||
// fn extract_bytecode(input_file: &str, output_file: &str) { | ||
// let contents = serde_json::from_slice::<Value>(&std::fs::read(input_file).unwrap()).unwrap(); | ||
// let bytecode = contents | ||
// .get("bytecode") | ||
// .unwrap() | ||
// .get("object") | ||
// .unwrap() | ||
// .as_str() | ||
// .unwrap() | ||
// .strip_prefix("0x") | ||
// .unwrap(); | ||
// let bytecode = alloy_primitives::hex::decode(bytecode).unwrap(); | ||
// std::fs::write(output_file, bytecode).unwrap(); | ||
// } |
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,21 @@ | ||
pragma solidity ^0.8.20; | ||
|
||
contract Account7702 { | ||
constructor() {} // TODO need owner | ||
|
||
struct Call { | ||
bytes data; | ||
address to; | ||
uint256 value; | ||
bytes signature; // TODO proper type | ||
} | ||
|
||
function execute(Call[] calldata calls) external payable { | ||
// TODO how to authenticate signture | ||
for (uint256 i = 0; i < calls.length; i++) { | ||
Call memory call = calls[i]; | ||
(bool success, ) = call.to.call{value: call.value}(call.data); | ||
require(success, "call reverted"); | ||
} | ||
} | ||
} |
Submodule safe-modules
added at
bc76ff
Submodule safe-smart-account
added at
c266ff
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 |
---|---|---|
@@ -1,2 +1,3 @@ | ||
pub mod nonce; | ||
pub mod safe; | ||
pub mod simple_account; |
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,115 @@ | ||
use alloy::{ | ||
dyn_abi::DynSolValue, | ||
primitives::{address, Address, Bytes, U256}, | ||
sol, | ||
sol_types::SolCall, | ||
}; | ||
|
||
sol!( | ||
#[allow(missing_docs)] | ||
#[sol(rpc)] | ||
SafeProxyFactory, | ||
"safe-smart-account/build/artifacts/contracts/proxies/SafeProxyFactory.sol/SafeProxyFactory.json" | ||
// "../../target/.foundry/forge/out/SafeProxyFactory.sol/SafeProxyFactory.json" | ||
// concat!(env!("OUT_DIR"), "/../../../../.foundry/forge/out/SafeProxyFactory.sol/SafeProxyFactory.json") | ||
); | ||
|
||
sol!( | ||
#[allow(clippy::too_many_arguments)] | ||
#[allow(missing_docs)] | ||
#[sol(rpc)] | ||
Safe, | ||
"safe-smart-account/build/artifacts/contracts/Safe.sol/Safe.json" | ||
); | ||
|
||
// https://github.com/WalletConnect/secure-web3modal/blob/c19a1e7b21c6188261728f4d521a17f94da4f055/src/core/SmartAccountSdk/utils.ts#L178 | ||
// https://github.com/WalletConnect/secure-web3modal/blob/c19a1e7b21c6188261728f4d521a17f94da4f055/src/core/SmartAccountSdk/constants.ts#L24 | ||
const SEPOLIA_SAFE_ERC_7579_LAUNCHPAD_ADDRESS: Address = | ||
address!("EBe001b3D534B9B6E2500FB78E67a1A137f561CE"); | ||
const SEPOLIA_SAFE_4337_MODULE_ADDRESS: Address = | ||
address!("3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2"); | ||
|
||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L438C36-L438C76 | ||
const SAFE_MULTI_SEND_ADDRESS: Address = | ||
address!("38869bf66a61cF6bDB996A6aE40D5853Fd43B526"); | ||
|
||
// https://github.com/safe-global/safe-modules-deployments/blob/d6642d90659de19e54bb4a20d646b30bd0a51885/src/assets/safe-4337-module/v0.3.0/safe-module-setup.json#L7 | ||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L431 | ||
const SAFE_MODULE_SETUP_ADDRESS: Address = | ||
address!("2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47"); | ||
|
||
sol!( | ||
#[allow(missing_docs)] | ||
#[sol(rpc)] | ||
SafeModuleSetup, | ||
"safe-modules/modules/4337/build/artifacts/contracts/SafeModuleSetup.sol/SafeModuleSetup.json" | ||
); | ||
|
||
sol!( | ||
#[allow(missing_docs)] | ||
#[sol(rpc)] | ||
MultiSend, | ||
"safe-smart-account/build/artifacts/contracts/libraries/MultiSend.sol/MultiSend.json" | ||
); | ||
|
||
// https://github.com/WalletConnect/secure-web3modal/blob/c19a1e7b21c6188261728f4d521a17f94da4f055/src/core/SmartAccountSdk/constants.ts#L10 | ||
// const APPKIT_SALT: U256 = U256::from_str("zg3ijy0p46"); | ||
|
||
fn encode_internal_transaction( | ||
to: Address, | ||
data: Vec<u8>, | ||
value: U256, | ||
operation: bool, | ||
) -> Bytes { | ||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L486 | ||
DynSolValue::Tuple(vec![ | ||
DynSolValue::Uint(U256::from(if operation { 1 } else { 0 }), 8), | ||
DynSolValue::Address(to), | ||
DynSolValue::Uint(value, 256), | ||
DynSolValue::Uint(U256::from(data.len()), 256), | ||
DynSolValue::Bytes(data), | ||
]) | ||
.abi_encode() | ||
.into() | ||
} | ||
|
||
fn init_code_call_data( | ||
owner: Address, | ||
) -> SafeProxyFactory::createProxyWithNonceCall { | ||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L714C31-L714C46 | ||
let enable_modules = SafeModuleSetup::enableModulesCall { | ||
modules: vec![SEPOLIA_SAFE_4337_MODULE_ADDRESS], | ||
} | ||
.abi_encode(); | ||
|
||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L486 | ||
let txn = encode_internal_transaction( | ||
SAFE_MODULE_SETUP_ADDRESS, | ||
enable_modules, | ||
U256::ZERO, | ||
true, | ||
); // TODO join any setupTransactions | ||
|
||
let multi_send_call_data = | ||
MultiSend::multiSendCall { transactions: txn }.abi_encode().into(); | ||
|
||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L728 | ||
let initializer = Safe::setupCall { | ||
_owners: vec![owner], | ||
_threshold: U256::from(1), | ||
to: SAFE_MULTI_SEND_ADDRESS, | ||
data: multi_send_call_data, | ||
fallbackHandler: SAFE_MODULE_SETUP_ADDRESS, | ||
paymentToken: Address::ZERO, | ||
payment: U256::ZERO, | ||
paymentReceiver: Address::ZERO, | ||
} | ||
.abi_encode() | ||
.into(); | ||
// https://github.com/pimlicolabs/permissionless.js/blob/b8798c121eecba6a71f96f8ddf8e0ad2e98a3236/packages/permissionless/accounts/safe/toSafeSmartAccount.ts#L840 | ||
SafeProxyFactory::createProxyWithNonceCall { | ||
_singleton: SEPOLIA_SAFE_ERC_7579_LAUNCHPAD_ADDRESS, | ||
initializer, | ||
saltNonce: U256::ZERO, | ||
} | ||
} |
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,20 @@ | ||
fn format_foundry_dir(path: &str) -> String { | ||
format!( | ||
"{}/../../../../.foundry/{}", | ||
std::env::var("OUT_DIR").unwrap(), | ||
path | ||
) | ||
} | ||
|
||
pub fn spawn_anvil() -> (AnvilInstance, String, ReqwestProvider, SigningKey) { | ||
let anvil = Anvil::at(format_foundry_dir("bin/anvil")).spawn(); | ||
let rpc_url = anvil.endpoint(); | ||
let provider = ReqwestProvider::<Ethereum>::new_http(anvil.endpoint_url()); | ||
let private_key = anvil.keys().first().unwrap().clone(); | ||
( | ||
anvil, | ||
rpc_url, | ||
provider, | ||
SigningKey::from_bytes(&private_key.to_bytes()).unwrap(), | ||
) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved this to
safe.rs
rather thanmod.rs
as I find it easier find things and navigate Rust codebasesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually don't like the module organization you are using. I find files by navigating in the file tree, not by searching for them. This current module structure has related files spread out.