From f5362e47164249f029e85e642ece3127f3b46484 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Sat, 11 Nov 2023 11:02:19 +0000 Subject: [PATCH] Configurable number of threads and explorer URL --- .devcontainer/devcontainer.json | 4 ++ Cargo.toml | 1 + README.md | 11 ++-- cli/src/chain.rs | 14 ++++- cli/src/main.rs | 90 +++++++++++++++++++++------------ core/src/safe.rs | 17 +++---- 6 files changed, 84 insertions(+), 53 deletions(-) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..77d2a49 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Rust", + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bookworm" +} diff --git a/Cargo.toml b/Cargo.toml index ff7a41a..6d045e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ members = [ "core", "wasm", ] +resolver = "2" diff --git a/README.md b/README.md index 903531c..75350d5 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ The transaction can be executed from any account (it can be done in MetaMask dir Go to Settings -> Advanced and enable `Show hex data`. When you go to create a transaction you will have a new optional field labelled `Hex data`. -Send a `0eth` transaction to the factory address, placing the generated calldata in the `Hex data` field. +Send a 0Ξ transaction to the factory address, placing the generated calldata in the `Hex data` field. Metamask will recognise it as a contract interaction in the confirmation step. @@ -86,11 +86,9 @@ Metamask will recognise it as a contract interaction in the confirmation step. 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. +1. Visit the `factory` URL from the command output. The link should open up the explorer at the correct location, if not click on _Contract_ > _Write Contract_. +2. Click on _Connect to Web3_ to connect the account you wish to pay for the Safe creation. +3. Fill the fields for the function _3. createProxyWithNonce (0x1688f0b9)_ using the generated outputs. ## Unsupported Chains @@ -108,7 +106,6 @@ deadbeef ... \ **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 🥦? Of course! diff --git a/cli/src/chain.rs b/cli/src/chain.rs index 7d81686..51d90bf 100644 --- a/cli/src/chain.rs +++ b/cli/src/chain.rs @@ -17,8 +17,8 @@ impl Chain { Self(1) } - /// Returns the [`Contracts`] for this chain, or `None` if the chain is not - /// supported. + /// Returns the [`Contracts`] for this chain, or [`None`] if the chain is + /// not supported. pub fn contracts(&self) -> Option { // Addresses can be found in the Safe deployments repository: // @@ -52,6 +52,16 @@ impl Chain { _ => None, } } + + /// Returns the URL of the block explorer for the chain, or [`None`] if the + /// chain is not supported. + pub fn explorer(&self) -> Option<&str> { + match self.0 { + 1 => Some("https://etherscan.io"), + 100 => Some("https://gnosisscan.io"), + _ => None, + } + } } impl Default for Chain { diff --git a/cli/src/main.rs b/cli/src/main.rs index 5f21b23..9069fd9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,11 +4,15 @@ use self::chain::Chain; use clap::Parser; use deadbeef_core::{Address, Contracts, Safe}; use hex::FromHexError; -use std::{process, str::FromStr, sync::mpsc, thread}; +use std::{num::NonZeroUsize, process, str::FromStr, sync::mpsc, thread}; /// Generate vanity addresses for Safe deployments. #[derive(Clone, Parser)] struct Args { + /// The number of parallel threads to use. Defaults to the number of CPUs. + #[arg(short = 'n', long, default_value_t = num_cpus::get())] + threads: usize, + /// Safe owners. /// /// Can be specified multiple times in order to specify multiple owners. @@ -42,21 +46,27 @@ struct Args { #[arg(long)] singleton: Option
, - /// Override for the Safe fallback handler address. + /// Override for the fallback handler address. #[arg(long)] fallback_handler: Option
, /// Quiet mode. /// /// Only output the transaction calldata without any extra information. - #[arg(short, long)] + #[arg(short, long, conflicts_with = "params")] quiet: bool, - /// Params mode. + /// Parameters mode. /// - /// Only output the needed fields for direct contract interaction. - #[arg(short = 'P', long)] + /// Only output the parameters for the calling the `createProxyWithNonce` + /// function on the `SafeProxyFactory`. + #[arg(short = 'P', long, conflicts_with = "quiet")] params: bool, + + /// Override the block explorer URL for generating a link to the + /// `SafeProxyFactory` contract. Can only be specified in parameters mode. + #[arg(long, requires = "params")] + explorer: Option, } /// Helper type for parsing hexadecimal byte input from the command line. @@ -74,6 +84,7 @@ impl FromStr for Hex { fn main() { let args = Args::parse(); + let threads = NonZeroUsize::new(args.threads); let contracts = args .chain .contracts() @@ -96,38 +107,53 @@ fn main() { }) }) .expect("unsupported chain"); - - let (sender, receiver) = mpsc::channel(); - let threads = (0..num_cpus::get()) - .map(|_| { - thread::spawn({ - let mut safe = Safe::new(contracts.clone(), args.owners.clone(), args.threshold); - let prefix = args.prefix.0.clone(); - let result = sender.clone(); - move || { - deadbeef_core::search(&mut safe, &prefix); - let _ = result.send(safe); - } + let explorer = args.explorer.as_deref().or_else(|| args.chain.explorer()); + + let setup = || { + ( + Safe::new(contracts.clone(), args.owners.clone(), args.threshold), + args.prefix.0.clone(), + ) + }; + let safe = if let Some(threads) = threads { + let (sender, receiver) = mpsc::channel(); + let _threads = (0..threads.get()) + .map(|_| { + thread::spawn({ + let (mut safe, prefix) = setup(); + let result = sender.clone(); + move || { + deadbeef_core::search(&mut safe, &prefix); + let _ = result.send(safe); + } + }) }) - }) - .collect::>(); + .collect::>(); + receiver.recv().expect("missing result") + } else { + let (mut safe, prefix) = setup(); + deadbeef_core::search(&mut safe, &prefix); + safe + }; - let safe = receiver.recv().expect("missing result"); let transaction = safe.transaction(); - let initializer_hex = hex::encode(safe.initializer()); + let factory = explorer + .map(|explorer| { + format!( + "{}/address/{}#writeContract#F3", + explorer, contracts.proxy_factory + ) + }) + .unwrap_or_else(|| contracts.proxy_factory.to_string()); if args.quiet { println!("0x{}", hex::encode(&transaction.calldata)); } else if args.params { - 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())); + println!("address: {}", safe.creation_address()); + println!("factory: {}", factory); + println!("singleton: {}", contracts.singleton); + println!("initializer: 0x{}", hex::encode(safe.initializer())); + println!("salt nonce: 0x{}", hex::encode(safe.salt_nonce())); } else { println!("address: {}", safe.creation_address()); println!("factory: {}", contracts.proxy_factory); @@ -139,9 +165,7 @@ fn main() { } println!("threshold: {}", args.threshold); println!("calldata: 0x{}", hex::encode(&transaction.calldata)); - } - let _ = threads; process::exit(0); } diff --git a/core/src/safe.rs b/core/src/safe.rs index fe05025..ea0abab 100644 --- a/core/src/safe.rs +++ b/core/src/safe.rs @@ -10,9 +10,9 @@ pub struct Safe { contracts: Contracts, owners: Vec
, threshold: usize, + initializer: Vec, salt: [u8; 64], create2: Create2, - initializer_calldata: Vec, } /// Safe contract data on a given chain. @@ -40,13 +40,11 @@ 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); + let initializer = contracts.initializer(&owners, threshold); - // Compute the salt using the initializer calldata let mut salt = [0_u8; 64]; let mut hasher = Keccak::v256(); - hasher.update(&initializer_calldata); + hasher.update(&initializer); hasher.finalize(&mut salt[0..32]); let mut create2 = Create2::new( @@ -54,9 +52,7 @@ impl Safe { Default::default(), contracts.proxy_init_code_digest(), ); - - // Update the salt for the create2 instance - hasher = Keccak::v256(); + let mut hasher = Keccak::v256(); hasher.update(&salt); hasher.finalize(create2.salt_mut()); @@ -64,9 +60,9 @@ impl Safe { contracts, owners, threshold, + initializer, salt, create2, - initializer_calldata, // Store the initializer data here } } @@ -82,7 +78,7 @@ impl Safe { /// Returns the initializer calldata for the Safe. pub fn initializer(&self) -> &[u8] { - &self.initializer_calldata + &self.initializer } /// Updates the salt nonce and recomputes the `CREATE2` salt. @@ -160,7 +156,6 @@ impl Contracts { buffer.extend_from_slice(&[0_u8; 28]); // padding buffer } - } /// Poor man's ABI encode.