Prerequisites
- A program that compiles and simulates. See Quickstart.
- The
saucecommand, installed as in Installation. - The release’s engine and Kitchen deployed on a chain the
saucecommand sends on.sauce pot --chain <chain> --owner <address>reports both.
The release’s contracts have code on your chain
A cook runs on the engine paired with the compiler in your package, through the Kitchen and your Pot. The SDK records the release’s contract addresses. A recorded address does not prove that code is deployed there on a given chain.sauce pot reads the chain and reports the engine, the Kitchen and your Pot:
sauce pot says so, and no cook can run there:
sauce pot says so after printing the Pot’s address:
sauce pot --chain <chain> --deploy and your signer, or let sauce run --fund deploy it before it funds it. Until a Pot is deployed, its address is the Kitchen’s prediction: a different Kitchen predicts a different address. Funds sent where the Kitchen has no code cannot be spent until a matching Kitchen exists. Run sauce pot with an endpoint: --offline derives the address and reads nothing. A preview chain can have both contracts deployed and still print an address that the sauce command and the MCP server refuse to deploy or cook on; see Supported chains.
To check an address yourself, ask your endpoint for its code with eth_getCode:
0x means no code at that address. Deployments lists the engine, Kitchen and Router addresses.
An engine runs only a Pot that its paired Kitchen created. A balance never moves between Pots on its own.
Simulate at the current block
Simulate the file you are about to send, with the same arguments, from the owner who sends it.sauce pools or sauce price describes one block. Simulate with --block <number> set to that block, then simulate again without it, at latest, right before you send. The chain can still move between your last simulation and the block that includes the cook. The bounds in the program, below, are what holds the cook to your prices then.
--assume-balance exists only in the simulation. Before sending, deploy the Pot and then fund it, or pass --fund, which deploys the Pot first, and simulate that path too. See Funding.
Minimum outputs and price limits
Give every swap a minimum output, a price limit, or both, asmain parameters:
swap.sauce.ts
minOut, the cook reverts. A non-zero sqrtPriceLimitX96 stops the swap at that price, and the input the pool did not use stays in the Pot. A swap with a zero limit and a zero minimum accepts any price. sauce price and sauce pools compute the limits. Quote, swap and supply shows a full example.
Amounts are in the token’s smallest unit: 1000000 is 1 USDC.
require guards for the conditions you rely on
Your program enforces the conditions you write. Before each step that depends on a balance, a price or a time, add a require with a message:
pay.sauce.ts
require reverts the whole cook. Check that each guard fires with --expect-revert, which exits 0 only when the cook reverts with a reason containing the text:
Int256(value) or Uint256(value) conversion also reverts the cook, with no message. Put a require before a conversion that can fail, so the revert says why.
Read the results of the calls you rely on
On EVM chains, the result of a contract call that the program does not read is not decoded or checked. An empty or malformed reply, or a call to an address with no code, then passes. To rely on a call:- read its result, which decodes and checks every declared output;
- check
ctx.isContract(address)where the target must have code; - move tokens with the SDK’s checked writes (
USDC.transfer,Token(address).approve, the@sauce/tokenfunctions), which reject a refused transfer and a target with no code. A rawIERC20.at(token).transfer(...)with its result discarded checks neither.
Catch boundaries
A cook is one transaction. An uncaught failure reverts every step of it, and the Kitchen’s fee is not paid. On EVM chains a program can catch a failure, and the catch decides what is undone:- A
tryblock of several statements rolls back all of its own calls, writes and assignments beforecatchruns. Work before the block stays. - A
tryaround one external call, or.catch()on a call, covers that call’s failure only. Decoding a successful reply is outside it. - The handler receives at most 256 bytes of revert data, possibly empty or truncated. Check its length before decoding it.
- Solana programs cannot catch: any failure reverts the cook.
guarded.sauce.ts
Approvals outside the cook’s rollback
A revert, or atry block’s rollback, covers the cook alone:
- An approval sent from your wallet in an earlier transaction stands after a reverted cook.
--fund-mode allowancesends one.--fund-mode permit2, the default, gives the Pot no allowance. - An approval a program makes from the Pot, and does not roll back, outlives the cook that made it. Approve exactly what the next call spends.
USDC.approve, the @sauce/token functions and the @sauce/router swaps) follow OpenZeppelin’s SafeERC20 rules: approve resets a standing allowance to zero before it sets the new amount. A raw IERC20.at(token).approve(...) call is not checked this way.
The Kitchen execution fee
Every successful cook through the Kitchen pays its execution fee, regardless of the program’s operations:- On the EVM chains it is paid in the chain’s native coin as the cook’s value; on Arc and Arc Testnet that coin is USDC. On the testnets
sauce potread it as 0 (Testnets gives the blocks). - The Kitchen’s owner sets the fee onchain, and it can change.
sauce tx,sauce runandsauce simulateread it when they build the cook. A cook sent with less reverts withFeeNotPaid. - A cook that reverts pays no fee.
- On Solana the fee is in lamports.
--max-execution-fee <lamports>refuses a cook whose fee is above your cap.
sauce pot prints the current fee, as above. The wallet that sends the cook pays the gas and the fee. The first run also pays gas for the Pot’s deployment, which a simulation reports as step 1. On Arc, sauce pot and sauce tx also print the minimum fee cap a transaction needs to be included.
Wallet policy limits
A wallet thesauce command keeps is a hot wallet: whoever holds its file and its password holds the key. Keep small balances in it. For larger amounts, print the transaction with sauce tx and sign it with a wallet that keeps its own key. See External wallets.
Give each wallet a policy, <name>.policy.json in the wallet directory:
ops.policy.json
- Amounts are whole tokens (
"5"is 5 USDC). A trailingngives base units ("5000000n"). tolists the addresses a transaction may call. On EVM chains that is usually your Pot. It does not limit whom the program pays: the program and your review do that.- Before each send, the command simulates the transaction and measures what leaves the wallet and its Pot, and the allowances it leaves other keys. It refuses a send that breaks a limit, and a send it cannot measure where a limit needs the measurement. A token limit also refuses an approval of Permit2 for that token, so fund such a wallet’s runs with
--fund-mode permit,allowanceortransfer. - The simulation runs on the chain’s head a moment before the send. A bound that must hold in the block the transaction lands in is a
requirein the program. chmod 600the policy file and any password file. The command refuses a password file that others can read or write, and a policy file that others can write.
--password-file or --password-command. Never put a key on the command line. See Wallets and policy.
When another signer signs, check the unsigned cook against your file before signing it:
sauce inspect confirms the program in the call is the one your file compiles to, and decodes the arguments after it. The transaction’s to must be your Pot.
Your own endpoints
The built-in public endpoints are for trying things. They can be slow or rate-limited. For production use, configure your own endpoint for each chain. Keep configuration containing provider credentials out of version control:sauce.config.json
eth_simulateV1. Without it, a simulation reports no transfers, and a wallet policy that needs a measurement refuses the send. See Chains and endpoints.
Supported chains
Thesauce command and the MCP server build and send transactions on Ethereum, Base, Arc and Solana and the testnets Arc Testnet, Base Sepolia, Ethereum Sepolia, Solana Devnet and Solana Testnet only. On every other chain a program compiles and the reads work, and a simulation runs wherever the release’s engine and Kitchen are deployed, but building or sending a transaction is refused before anything is signed. Send on a supported chain. See Chains.
Next steps
- Troubleshooting: what each refusal and revert means.
- Package updates: recompile and simulate again after updating the package.
- Security considerations: what the Pot, the Kitchen and the engine enforce.
