Skip to content

Latest commit

 

History

History
108 lines (81 loc) · 4.33 KB

README.md

File metadata and controls

108 lines (81 loc) · 4.33 KB

ERC20 Example

An example that uses the ERC20 interface's balanceOf method.

Prerequisites

To get started, you need to have Rust installed. If you haven't done so, follow the instructions here.

Next, install the cargo risczero tool. We'll use cargo binstall to facilitate this. Detailed instructions can be found at cargo-binstall.

cargo install cargo-binstall
cargo binstall cargo-risczero

Finally, install the risc0 toolchain with the following command:

cargo risczero install

You'll also need access to an Ethereum RPC node, such as through Alchemy.

Run the example

To run the example, which queries the USDT balance of 0x9737100D2F42a196DE56ED0d1f6fF598a250E7E4 on Sepolia, execute the following command:

RPC_URL=https://eth-sepolia.g.alchemy.com/v2/<API_KEY> RUST_LOG=info cargo run --release

The output should resemble the following:

2024-06-04T09:32:22.119650Z  INFO risc0_steel::contract: Executing preflight for 'balanceOf(address)' on contract 0xaA8E23Fb1079EA71e0a56F48a2aA51851D8433D0    
For block 6037045 `balanceOf(address)` returns: 399534748753251
Running the guest with the constructed input:
View call result: 399534748753251
2024-06-04T09:32:24.152552Z  INFO executor: risc0_zkvm::host::server::exec::executor: execution time: 218.576666ms
2024-06-04T09:32:24.152578Z  INFO executor: risc0_zkvm::host::server::session: number of segments: 5
2024-06-04T09:32:24.152581Z  INFO executor: risc0_zkvm::host::server::session: total cycles: 5242880
2024-06-04T09:32:24.152583Z  INFO executor: risc0_zkvm::host::server::session: user cycles: 4126244

Guest Code

Here is a snippet of the relevant code of the guest:

/// Specify the function to call using the [`sol!`] macro.
/// This parses the Solidity syntax to generate a struct that implements the `SolCall` trait.
sol! {
    /// ERC-20 balance function signature.
    interface IERC20 {
        function balanceOf(address account) external view returns (uint);
    }
}

/// Function to call, implements the `SolCall` trait.
const CALL: IERC20::balanceOfCall = IERC20::balanceOfCall {
    account: address!("9737100D2F42a196DE56ED0d1f6fF598a250E7E4"),
};

/// Address of the deployed contract to call the function on (USDT contract on Sepolia).
const CONTRACT: Address = address!("aA8E23Fb1079EA71e0a56F48a2aA51851D8433D0");
/// Address of the caller. If not provided, the caller will be the [CONTRACT].
const CALLER: Address = address!("f08A50178dfcDe18524640EA6618a1f965821715");

fn main() {
    // Read the input from the guest environment.
    let input: EthEvmInput = env::read();

    // Converts the input into a `EvmEnv` for execution. The `with_chain_spec` method is used
    // to specify the chain configuration. It checks that the state matches the state root in the
    // header provided in the input.
    let env = input.into_env().with_chain_spec(&ETH_SEPOLIA_CHAIN_SPEC);
    // Commit the block hash and number used when deriving `EvmEnv` to the journal.
    env::commit_slice(&env.block_commitment().abi_encode());

    // Execute the view call; it returns the result in the type generated by the `sol!` macro.
    let contract = Contract::new(CONTRACT, &env);
    let returns = contract.call_builder(&CALL).from(CALLER).call();
    println!("View call result: {}", returns._0);
}

Host Code

Here is a snippet to the relevant code on the host, it requires the same arguments as the guest:

// Create an EVM environment from an RPC endpoint and a block number. If no block number is
// provided, the latest block is used.
let mut env = EthEvmEnv::from_rpc(&args.rpc_url, None)?;
//  The `with_chain_spec` method is used to specify the chain configuration.
env = env.with_chain_spec(&ETH_SEPOLIA_CHAIN_SPEC);

// Preflight the call to prepare the input that is required to execute the function in
// the guest without RPC access. It also returns the result of the call.
let mut contract = Contract::preflight(CONTRACT, &mut env);
let returns = contract.call_builder(&CALL).from(CALLER).call()?;

// Finally, construct the input from the environment.
let input = env.into_input()?;