> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boundless.network/llms.txt
> Use this file to discover all available pages before exploring further.

# How does Steel work?

> A guide to how Steel works.

A fundamental operation in smart contracts is to look up data from other contracts, such as the ERC20 token balance of a specific address. This operation is known as a “view call” - it “views” state without altering it. Steel allows developers to query EVM state, within the zkVM, by just defining the Solidity method they wish to view call (using alloy’s [sol! macro](https://alloy.rs/contract-interactions/using-sol%21/)).

```rust theme={null}
alloy::sol!(
    interface IERC20 {
        function balanceOf(address account) external view returns (uint);
}
);
```

*This code is taken from the erc20-counter example, which you can find [here](https://github.com/boundless-xyz/steel/tree/main/examples/erc20-counter).*

The sol! macro parses Solidity syntax to generate Rust types; this is used to call the `balanceOf` function, within the [guest program](https://dev.risczero.com/terminology#guest-program), using `balanceOfCall`:

```rust theme={null}
// GUEST PROGRAM
// Read the input from the guest environment.
let input: EthEvmInput = env::read();
let contract: Address = env::read();
let account: Address = env::read();

let evm_env = input.into_env(&ETH_SEPOLIA_CHAIN_SPEC);

// Execute the view call; it returns the result in the type generated by the `sol!` macro.
let call = IERC20::balanceOfCall { account };
let balance = Contract::new(contract, &evm_env)
  .call_builder(&call)
  .call();

// Check that the given account holds at least 1 token.
assert!(balance >= U256::from(1));

// Commit the block hash and number used when deriving `view_call_env` to the journal.
let journal = Journal {
    commitment: evm_env.into_commitment(),
    tokenAddress: contract,
};
env::commit_slice(&journal.abi_encode());
```

## Proving smart contract execution within the zkVM

The zkVM guest has no network connection, and there is no way to call an RPC provider to carry out the view call from within the guest; so how does Steel make this possible?

Steel’s key innovation is the use of [revm](https://docs.rs/revm/latest/revm/) for simulation of an *EVM environment* within the guest program. This EVM environment has the necessary state populated from RPC calls, and verified with Merkle storage proofs, to carry out verifiable execution of view calls. In the [host program](https://dev.risczero.com/terminology#host-program), the [preflight call](https://risc0.github.io/risc0-ethereum/risc0_steel/struct.Contract.html#method.preflight) constructs the EVM environment, `evm_env` which is passed through as input to the guest program:

```rust theme={null}
// HOST PROGRAM
// Create an alloy provider from RPC URL
let provider = ProviderBuilder::new()
    .connect_http(eth_rpc_url);

// Create an EVM environment from that provider defaulting to the latest block.
let mut env = EthEvmEnv::builder()
    .chain_spec(&ETH_SEPOLIA_CHAIN_SPEC)
    .provider(provider.clone())
    .build()
    .await?;

// Preflight the call to prepare the input that is required to execute the function in the guest without RPC access.
let mut contract = Contract::preflight(token_contract, &mut env);

let evm_input = env.into_input().await?;
```

The `preflight` step calls the RPC provider for the necessary state and for the Merkle storage proofs via `eth_getProof` ([EIP-1186](https://eips.ethereum.org/EIPS/eip-1186)). These Merkle proofs are given to the guest which verifies them to prove that the RPC data is valid, without having to run a full node and without trusting the host or RPC provider.

## Verifying the proof onchain

At this point, we have generated a proof of: a view call of state onchain and some execution based on that view call state (e.g. checking that the balance is at least 1).

When using Steel, the general pattern for onchain functions incorporating Steel follows this pseudo-code:

```solidity theme={null}
contract {
    function doSomething(journalData, proof) {
        validate journal data
        validate Steel commitment

        verify proof
        doSomethingElse()
    }
}
```

The interesting onchain logic, *doSomethingElse()*, is only reached if the journal data, the steel commitment and the proof are all valid.

Concretely, in the erc20-counter example, the counter is only updated if the caller has a balance of at least one, and this counter update is gated by Steel and the zkVM.

```solidity theme={null}
contract Counter {

  function increment(bytes calldata journalData, bytes calldata seal) external {
      // Decode and validate the journal data
      Journal memory journal = abi.decode(journalData, (Journal));
      require(journal.tokenContract == tokenContract, "Invalid token address");
      require(Steel.validateCommitment(journal.commitment), "Invalid commitment");

      // Verify the proof
      bytes32 journalHash = sha256(journalData);
      verifier.verify(seal, imageID, journalHash);

      // If the balance is at least one, update the counter
      counter += 1;
  }
}
```

Within a single proof, we’ve seen Steel can handle view calls orders of magnitude larger than onchain execution can handle. Specifically, one partner application has shown gas savings of 1.2 *billion* gas for a contract call using around 400,000 SLOADs. 1.2 billion gas is around 30 *blocks* worth of execution and this can be verified onchain in one proof, that costs under \$10 to generate, and less than 300k gas to verify (see [RISC Zero’s verification contracts](https://dev.risczero.com/api/blockchain-integration/contracts/verifier)).

With proof aggregation, cost savings are amortized even further, by taking multiple separate applications of RISC Zero, and wrapping them all up into a single SNARK.
