From 0fcc7ab2dff86850c9df166d39ece07d56ab919b Mon Sep 17 00:00:00 2001 From: Kennedy Baird Date: Fri, 10 Nov 2023 10:12:49 +1300 Subject: [PATCH 1/5] feat: add inputs argument --- cli/src/main.rs | 18 ++++++++++++++++++ core/src/safe.rs | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index eac3b54..7fd9aa9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -51,6 +51,12 @@ struct Args { /// Only output the transaction calldata without any extra information. #[arg(short, long)] quiet: bool, + + /// Quiet mode. + /// + /// Only output the transaction calldata without any extra information. + #[arg(short, long)] + inputs: bool, } /// Helper type for parsing hexadecimal byte input from the command line. @@ -108,9 +114,20 @@ fn main() { let safe = receiver.recv().expect("missing result"); let transaction = safe.transaction(); + let initializer_hex = hex::encode(safe.initializer()); if args.quiet { println!("0x{}", hex::encode(&transaction.calldata)); + } else if args.inputs { + println!("address: {}", safe.creation_address()); + println!("owners: {}", args.owners[0]); + for owner in &args.owners[1..] { + println!(" {}", owner); + } + println!("--------------------------------------------------------"); + println!("_singleton: {}", contracts.singleton); + println!("initializer: 0x{}", initializer_hex); + println!("saltNonce: 0x{}", hex::encode(&safe.salt_nonce())); } else { println!("address: {}", safe.creation_address()); println!("factory: {}", contracts.proxy_factory); @@ -122,6 +139,7 @@ fn main() { } println!("threshold: {}", args.threshold); println!("calldata: 0x{}", hex::encode(&transaction.calldata)); + } let _ = threads; diff --git a/core/src/safe.rs b/core/src/safe.rs index 5bef0e5..fe05025 100644 --- a/core/src/safe.rs +++ b/core/src/safe.rs @@ -12,6 +12,7 @@ pub struct Safe { threshold: usize, salt: [u8; 64], create2: Create2, + initializer_calldata: Vec, } /// Safe contract data on a given chain. @@ -39,9 +40,13 @@ pub struct Transaction { impl Safe { /// Creates a new safe from deployment parameters. pub fn new(contracts: Contracts, owners: Vec
, threshold: usize) -> Self { + // Compute the initializer calldata once and store it + let initializer_calldata = contracts.initializer(&owners, threshold); + + // Compute the salt using the initializer calldata let mut salt = [0_u8; 64]; let mut hasher = Keccak::v256(); - hasher.update(&contracts.initializer(&owners, threshold)); + hasher.update(&initializer_calldata); hasher.finalize(&mut salt[0..32]); let mut create2 = Create2::new( @@ -49,7 +54,9 @@ impl Safe { Default::default(), contracts.proxy_init_code_digest(), ); - let mut hasher = Keccak::v256(); + + // Update the salt for the create2 instance + hasher = Keccak::v256(); hasher.update(&salt); hasher.finalize(create2.salt_mut()); @@ -59,6 +66,7 @@ impl Safe { threshold, salt, create2, + initializer_calldata, // Store the initializer data here } } @@ -72,6 +80,11 @@ impl Safe { self.salt[32..64].try_into().unwrap() } + /// Returns the initializer calldata for the Safe. + pub fn initializer(&self) -> &[u8] { + &self.initializer_calldata + } + /// Updates the salt nonce and recomputes the `CREATE2` salt. pub fn update_salt_nonce(&mut self, f: impl FnOnce(&mut [u8])) { let salt_nonce = unsafe { self.salt.get_unchecked_mut(32..64) }; @@ -147,6 +160,7 @@ impl Contracts { buffer.extend_from_slice(&[0_u8; 28]); // padding buffer } + } /// Poor man's ABI encode. From 7544f68b8aaeab7c56a01c326e441bcba6f13bee Mon Sep 17 00:00:00 2001 From: Kennedy Baird Date: Sat, 11 Nov 2023 11:09:35 +1300 Subject: [PATCH 2/5] update to "params" --- cli/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 7fd9aa9..5f21b23 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -52,11 +52,11 @@ struct Args { #[arg(short, long)] quiet: bool, - /// Quiet mode. + /// Params mode. /// - /// Only output the transaction calldata without any extra information. - #[arg(short, long)] - inputs: bool, + /// Only output the needed fields for direct contract interaction. + #[arg(short = 'P', long)] + params: bool, } /// Helper type for parsing hexadecimal byte input from the command line. @@ -118,7 +118,7 @@ fn main() { if args.quiet { println!("0x{}", hex::encode(&transaction.calldata)); - } else if args.inputs { + } else if args.params { println!("address: {}", safe.creation_address()); println!("owners: {}", args.owners[0]); for owner in &args.owners[1..] { From 9d112c7faa8459e98dce077fb6b86b800f474e4d Mon Sep 17 00:00:00 2001 From: Kennedy Baird Date: Sat, 11 Nov 2023 11:15:59 +1300 Subject: [PATCH 3/5] chore: update readme to include params instruction --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be2c921..1abbefb 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ This feature is not officially supported by the tool. The above command will generate some [calldata](https://www.quicknode.com/guides/ethereum-development/transactions/ethereum-transaction-calldata) for creating a Safe with the specified owners and threshold. -To create the safe, simply execute a transaction to the [factory address](https://etherscan.io/address/0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67) with the generated calldata. +To create the safe, simply execute a transaction to the [factory address](https://etherscan.io/address/0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67) with the generated calldata, or use the `createProxyWithNonce` function on Etherscan. The transaction can be executed from any account (it can be done in MetaMask directly for example). ### Metamask Steps @@ -96,6 +96,17 @@ Send a `0eth` transaction to the factory address, placing the generated calldata Metamask will recognise it as a contract interaction in the confirmation step. +### Etherscan + +Use the `--params` flag to output contract-ready inputs. + +1. Visit the [factory address](https://etherscan.io/address/0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67). +2. Click Contract -> Write Contract -> Connect to Web3. +3. Connect the account you wish to pay for the Safe creation. + +Fill the fields in function `3. createProxyWithNonce` using the generated outputs. + + ## Is This Vegan Friendly 🥦? Of course! From ecb03fe83aec4a582f836b7dbf3fd9f997f92e90 Mon Sep 17 00:00:00 2001 From: Kennedy Baird Date: Sat, 11 Nov 2023 11:16:50 +1300 Subject: [PATCH 4/5] chore: suggest alternate order for readme --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1abbefb..4bbf784 100644 --- a/README.md +++ b/README.md @@ -65,22 +65,6 @@ As well as custom fallback handlers: deadbeef ... --fallback-handler 0x4e305935b14627eA57CBDbCfF57e81fd9F240403 ... ``` -## Unsupported Chains - -Safe deployments on non-officially supported networks can also be used by overriding all contract addresses and the proxy init code: - -``` -deadbeef ... \ - --chain $UNSUPPORTED_CHAIN \ - --proxy-factory 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ - --proxy-init-code 0xbb \ - --singleton 0xcccccccccccccccccccccccccccccccccccccccc \ - --fallback-handler 0xdddddddddddddddddddddddddddddddddddddddd -``` - -**Use this with caution**, this assumes that the proxy address is computed in the exact same was as on Ethereum, which may not be the case for all networks. -This feature is not officially supported by the tool. - ## Creating the Safe The above command will generate some [calldata](https://www.quicknode.com/guides/ethereum-development/transactions/ethereum-transaction-calldata) for creating a Safe with the specified owners and threshold. @@ -106,6 +90,22 @@ Use the `--params` flag to output contract-ready inputs. Fill the fields in function `3. createProxyWithNonce` using the generated outputs. +## Unsupported Chains + +Safe deployments on non-officially supported networks can also be used by overriding all contract addresses and the proxy init code: + +``` +deadbeef ... \ + --chain $UNSUPPORTED_CHAIN \ + --proxy-factory 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ + --proxy-init-code 0xbb \ + --singleton 0xcccccccccccccccccccccccccccccccccccccccc \ + --fallback-handler 0xdddddddddddddddddddddddddddddddddddddddd +``` + +**Use this with caution**, this assumes that the proxy address is computed in the exact same was as on Ethereum, which may not be the case for all networks. +This feature is not officially supported by the tool. + ## Is This Vegan Friendly 🥦? From 0da0a65379804a395347a65892f1a32a66ba202c Mon Sep 17 00:00:00 2001 From: Kennedy Baird Date: Sat, 11 Nov 2023 11:24:02 +1300 Subject: [PATCH 5/5] chore: suggest adding note about safe 1.3.0 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4bbf784..903531c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ For Ethereum: Since this version of the Safe proxy factory uses `CREATE2` op-code, we can change the final address by fiddling with the user-specified `saltNonce` parameter. It works by randomly trying out different values for the `saltNonce` parameter until it find ones that creates an address matching the desired prefix. +*Commit [`24df00b`](https://github.com/nlordell/deadbeef/tree/24df00bfb1e7fdb594be97c017cd627e643c5318) supports Safe `v1.3.0`.* + ## Building For longer prefixes, this can take a **very** long time, so be sure to build with release: