rollchad / stylus-create2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stylus create2 example

Contract factory is used to deploy counter with create2.

Steps

  1. Compile counter
cargo build -p counter --target wasm32-unknown-unknown --release
  1. Obtain init code for counter. This outputs file deployment_tx_data in the factory crate.
cargo stylus deploy --wasm-file-path ./target/wasm32-unknown-unknown/release/counter.wasm --dry-run --output-tx-data-to-dir ./crates/factory/src --mode deploy-only --private-key-path ./.privatekey
  1. The factory contract reads bytes from deployment_tx_data, then deploys it with create2.
#[external]
impl Factory {
    pub fn deploy_with_create2(&mut self) -> Result<(), Vec<u8>> {
        let init_code = include_bytes!("./deployment_tx_data");

        let salt = B256::default();
        let endowment = U256::ZERO;

        let actual_address = unsafe { RawDeploy::new().salt(salt).deploy(init_code, endowment)? };

        Ok(())
    }
}
  1. Deploy factory and call deploy_with_create2()
# compile
cargo build -p factory --target wasm32-unknown-unknown --release

# deploy factory- note the address
cargo stylus deploy --wasm-file-path ./target/wasm32-unknown-unknown/release/factory.wasm --private-key-path ./.privatekey

# setup PRIVATE_KEY and FACTORY_ADDRESS env variables
PRIVATE_KEY=yourkey

FACTORY_ADDRESS=deployedAddressFromDeployCommand

# call deploy_with_create2() with cast
cast send --rpc-url https://stylus-testnet.arbitrum.io/rpc --private-key $PRIVATE_KEY $FACTORY_ADDRESS "deployWithCreate2()"

Activation

Activation has a nifty hack. You only need to activate one instance of a contract. Subsequent copies with the same code will automatically be activated.

We deploy a new copy of counter with the CLI. This will also activate the counter instance deployed by our factory.

cargo stylus deploy --wasm-file-path ./target/wasm32-unknown-unknown/release/counter.wasm --private-key-path ./.privatekey

To test, lookup the instance address that was deployed from factory using blockscout, then use cast to send a transaction.

# gives 0
cast call --rpc-url https://stylus-testnet.arbitrum.io/rpc --private-key $PRIVATE_KEY $COUNTER_ADDRESS "number()"

# increment
cast send --rpc-url https://stylus-testnet.arbitrum.io/rpc --private-key $PRIVATE_KEY $COUNTER_ADDRESS "increment()"

Bonus- Deriving create2 address without deploying

The SDK lacked a function to derive create2 address, so I created one.

fn keccak256(bytes: &[u8]) -> [u8; 32] {
    use tiny_keccak::{Hasher, Keccak};

    let mut output = [0u8; 32];
    let mut hasher = Keccak::v256();
    hasher.update(bytes);
    hasher.finalize(&mut output);

    output
}

fn get_create2_address(from: Address, salt: B256, init_code: &[u8]) -> Address {
    let init_code_hash = keccak256(init_code);

    let mut bytes = Vec::with_capacity(1 + 20 + salt.len() + init_code_hash.len());

    bytes.push(0xff);
    bytes.extend_from_slice(from.as_slice());
    bytes.extend_from_slice(salt.as_slice());
    bytes.extend_from_slice(init_code_hash.as_slice());

    let hash = keccak256(bytes.as_slice());

    let mut address_bytes = [0u8; 20];
    address_bytes.copy_from_slice(&hash[12..]);

    let address = Address::from_slice(&address_bytes);

    address
}

About


Languages

Language:Rust 100.0%