Skip to main content
The EVM Pot exposes this contract function (Solidity declaration):
A cook is the transaction that runs one program. On an EVM chain it is a call to the owner’s Pot:

Who may call it

A private Pot accepts a direct cook call only from its owner. A direct call from another account reverts with NotOwner(). A Shared Pot, which has no owner, accepts a cook from anyone. A Pot created by the current Kitchen has a second entry point, cookSigned: the owner signs a cook as EIP-712 typed data, and any account submits it and pays the gas (Signed cooks). The sauce command builds the cook for the owner: sauce simulate runs it without a key, sauce run signs and sends it with the owner’s wallet, and sauce tx prints it unsigned for another signer. sauce sign signs a cook for another account to submit with cookSigned.

What the Pot does

  1. It checks that the sender is its owner, and reverts with NotOwner() otherwise. For cookSigned it checks the owner’s signature, the nonce, the deadline and the submitter instead (Signed cooks).
  2. It forwards the Kitchen’s fee to the Kitchen. For cook, the fee comes out of the cook’s value: a value below the fee reverts with FeeNotPaid(uint256 required, uint256 provided). For cookSigned, it comes out of the Pot’s own balance, which includes the value sent, up to the signed maxExecutionFee: a fee above the smaller of the two reverts with FeeNotPaid.
  3. It runs the program on engine, inside itself and on its own balances. The program runs as the Pot: pot and ctx.self() are the Pot, and every call the program makes comes from the Pot.
  4. It returns what main returns.
A cook runs exactly one program. The Pot keeps no engine between cooks: every cook names one.
pay.sauce.ts
That simulation ran on a local fork of Base at block 52965000 with the current contracts (abridged). The owner had no Pot yet, so the simulation deployed it first, for 667,003 gas.

The unsigned cook

sauce tx prints the cook as JSON for any wallet to sign. When the Pot has no code yet it prints a plan, the Pot’s deployment first:
That plan was read from a local fork of Base at block 52965000 with the current contracts (abridged; the output also carries sendCalls, both transactions as one batch for a wallet that sends batches).
  • data is 0xb38dbf14, the selector of cook(address,bytes), followed by the ABI encoding of the engine’s address and program. Here program is 287 bytes: the 223-byte program and two 32-byte arguments.
  • value is 3,744,785,386,350 wei, the Kitchen’s fee read when the cook was built. The program spends no ETH, so nothing is added.
  • Once the Pot has code, sauce tx prints the cook alone: chainId, to, data and value.
  • --format cast prints each transaction as a cast send line instead.

Atomicity

The program’s steps run in order inside the cook. An uncaught failure, such as a failed require, a call that reverts or a checked arithmetic error, reverts the whole cook, the Kitchen’s fee with it: nothing moves, and the sender pays only the gas used up to the failure. Simulated on a local fork of Base at block 52965000 with the current contracts, with no USDC in the Pot, the same cook gives:
On EVM chains a try/catch block narrows the rollback: when the block fails, its own calls, writes and assignments roll back, the handler runs, and the cook goes on; work before the block remains. Solana has no catch, so there every failure reverts the cook. The rollback covers this transaction only. What earlier transactions did stays done: an approval a previous cook gave outlives that cook, and its spender can still use it after a later cook reverts. Approve exactly what the next call spends.

Return values

What main returns is the cook’s return data. The ABI declares returns (bytes), but the bytes are the value itself, laid out by the type main declares, with no ABI bytes wrapper around them: An array is therefore not what an ABI decoder reads. To return ABI-encoded data, return abi.encode(...) as bytes. Solana has no abi.encode.
balance.sauce.ts
sauce simulate decodes the return data as the declared type. With 5 USDC assumed in the Pot, on a local fork of Base at block 53122782 with the current contracts:

Gas

On an EVM chain the wallet that sends the cook pays its gas in the chain’s native coin. A cook’s gas is the transaction’s, the Pot’s and the Kitchen’s, plus the program’s own, which the engine runs step by step. Measured through the released engine and a Pot the Kitchen made, on a local chain: The programs on these pages, simulated on a local fork of Base at block 52965000 with the current contracts:
  • The figure for your program is the simulation’s, on the chain as it is when you run it. A swap that crosses more of a pool’s price range than it did in the simulation spends more, so leave room between the simulated gas and the gas limit.
  • On Base and other OP Stack chains each transaction also pays a fee for publishing its data to Ethereum. sauce run counts it in what a run cost.
  • A cook that fails pays the gas it used up to the failure, and no Kitchen fee.

On Arc

Arc’s native coin is USDC, so a cook’s value, the Kitchen’s fee within it, and its gas are paid in USDC. As native value USDC has 18 decimals; it is the same balance as the 6-decimal USDC token. Simulated on a local fork of Arc at block 27376858 with the current contracts, checked.sauce.ts with --args 7 5:
Arc includes a transaction only with a maxFeePerGas (or gasPrice) of at least 20 gwei, its minimum base fee.

On Solana

A cook on Solana is an instruction, and there are two ways to send one: Both carry a signed max_execution_fee, and a fee above it refuses the cook before anything runs. The same compiled program runs either way, with the same account indices.
  • Compute. A transaction has a budget of 1,400,000 compute units, shared by the engine and every program it calls. A cook that runs out fails whole. pay.sauce.ts used 49,931 compute units in the simulation below.
  • Heap. Every cook requests a 256 KiB heap frame, and a program has 64 KiB of working memory per run. The sauce command and the SDK’s client add the request; a cook assembled by hand must carry it once.
  • Signature fees. Each signature costs 5,000 lamports. In the simulation below the owner paid 90,672 lamports for the cook: the Kitchen’s 85,672 and one signature.
  • Size. A cook too large for one transaction is staged first (Bytecode). sauce tx then prints a plan of steps: the setup, the cook, then closing the code buffer.
Simulated in the sauce command’s local copy (LiteSVM) of Solana at slot 460261665, with the current engine and Kitchen and 5 USDC assumed in a Pot not created yet (abridged):

Next steps