Skip to content
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

feat: add inputs argument #12

Merged
merged 6 commits into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -65,6 +67,31 @@ As well as custom fallback handlers:
deadbeef ... --fallback-handler 0x4e305935b14627eA57CBDbCfF57e81fd9F240403 ...
```

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

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

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.

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.

## Unsupported Chains

Safe deployments on non-officially supported networks can also be used by overriding all contract addresses and the proxy init code:
Expand All @@ -81,20 +108,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.

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

To create the safe, simply execute a transaction to the [factory address](https://etherscan.io/address/0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67) with the generated calldata.
The transaction can be executed from any account (it can be done in MetaMask directly for example).

### Metamask Steps

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.

Metamask will recognise it as a contract interaction in the confirmation step.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra newline

Suggested change

## Is This Vegan Friendly 🥦?

Expand Down
18 changes: 18 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ struct Args {
/// Only output the transaction calldata without any extra information.
#[arg(short, long)]
quiet: bool,

/// Params mode.
///
/// 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.
Expand Down Expand Up @@ -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.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()));
} else {
println!("address: {}", safe.creation_address());
println!("factory: {}", contracts.proxy_factory);
Expand All @@ -122,6 +139,7 @@ fn main() {
}
println!("threshold: {}", args.threshold);
println!("calldata: 0x{}", hex::encode(&transaction.calldata));

}

let _ = threads;
Expand Down
18 changes: 16 additions & 2 deletions core/src/safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct Safe {
threshold: usize,
salt: [u8; 64],
create2: Create2,
initializer_calldata: Vec<u8>,
}

/// Safe contract data on a given chain.
Expand Down Expand Up @@ -39,17 +40,23 @@ 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);

// 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(
contracts.proxy_factory,
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());

Expand All @@ -59,6 +66,7 @@ impl Safe {
threshold,
salt,
create2,
initializer_calldata, // Store the initializer data here
}
}

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

}

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