Skip to content

Commit

Permalink
Configurable number of threads and explorer URL
Browse files Browse the repository at this point in the history
  • Loading branch information
nlordell committed Nov 11, 2023
1 parent 5faa6eb commit f5362e4
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 53 deletions.
4 changes: 4 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "Rust",
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bookworm"
}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ members = [
"core",
"wasm",
]
resolver = "2"
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,17 @@ 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 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.

### 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.
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

Expand All @@ -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!
Expand Down
14 changes: 12 additions & 2 deletions cli/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Contracts> {
// Addresses can be found in the Safe deployments repository:
// <https://github.com/safe-global/safe-deployments/tree/main/src/assets/v1.4.1>
Expand Down Expand Up @@ -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 {
Expand Down
90 changes: 57 additions & 33 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -42,21 +46,27 @@ struct Args {
#[arg(long)]
singleton: Option<Address>,

/// Override for the Safe fallback handler address.
/// Override for the fallback handler address.
#[arg(long)]
fallback_handler: Option<Address>,

/// 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<String>,
}

/// Helper type for parsing hexadecimal byte input from the command line.
Expand All @@ -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()
Expand All @@ -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::<Vec<_>>();
.collect::<Vec<_>>();
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);
Expand All @@ -139,9 +165,7 @@ fn main() {
}
println!("threshold: {}", args.threshold);
println!("calldata: 0x{}", hex::encode(&transaction.calldata));

}

let _ = threads;
process::exit(0);
}
17 changes: 6 additions & 11 deletions core/src/safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ pub struct Safe {
contracts: Contracts,
owners: Vec<Address>,
threshold: usize,
initializer: Vec<u8>,
salt: [u8; 64],
create2: Create2,
initializer_calldata: Vec<u8>,
}

/// Safe contract data on a given chain.
Expand Down Expand Up @@ -40,33 +40,29 @@ pub struct Transaction {
impl Safe {
/// Creates a new safe from deployment parameters.
pub fn new(contracts: Contracts, owners: Vec<Address>, 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(
contracts.proxy_factory,
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());

Self {
contracts,
owners,
threshold,
initializer,
salt,
create2,
initializer_calldata, // Store the initializer data here
}
}

Expand All @@ -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.
Expand Down Expand Up @@ -160,7 +156,6 @@ impl Contracts {
buffer.extend_from_slice(&[0_u8; 28]); // padding
buffer
}

}

/// Poor man's ABI encode.
Expand Down

0 comments on commit f5362e4

Please sign in to comment.