Skip to main content
A program spends what its Pot holds. This page covers the flags that move tokens into the Pot as part of a cook (--fund, --fund-mode, --sweep), the flag that gives a simulation balances the Pot does not hold (--assume-balance), and the amount syntax both take.

Prerequisites

  • The sauce command: npm install -g @eco-incorp/sauce.
  • A program that spends from the Pot. The examples use pay.sauce.ts from sauce compile, which pays amount of USDC to to.
  • For a real run, a signer: a wallet from sauce wallet, a keystore or a key.
  • To simulate or run on an EVM chain, the release’s engine and Kitchen deployed on the chain, and to run, a chain the sauce command sends on. sauce pot --chain <chain> --owner <address> reports both, and on a preview chain refuses to build the Pot’s deployment.

Two ways to fund a Pot

  • Send tokens to the deployed Pot, from any wallet or exchange. On an EVM chain, deploy it first with sauce pot --chain <chain> --deploy and your signer; sauce pot prints its address and whether it is deployed.
  • Pull them in the cook with --fund. The pull, the program and the sweep are one transaction, so they succeed or revert together. sauce run --fund deploys a Pot that has no code before it funds it.
On an EVM chain, deploy the Pot before you send it funds from another wallet. An undeployed address is only the Kitchen’s prediction. sauce pot --chain <chain> --deploy, with your signer, deploys the Pot and waits for its code; sauce run --fund deploys it before it funds it. With an endpoint, a sauce tx --fund plan puts a token’s transfer after the deployment and before the cook. Without an endpoint, including with --offline, it checks no deployment and plans no deployment step: deploy the Pot first. ETH:<amount> adds to the cook’s value, with no separate transfer. Run sauce pot --chain <chain> --owner <owner> with an endpoint first: it reports whether the chain has the Kitchen, and on a preview chain it prints a Pot address but refuses the deployment, as the CLI and MCP refuse every transaction there. --offline checks neither. Sweep the Pot recovers funds from a deployed Pot through an owner-authorized cook.
With an endpoint, the command also holds the Pot to the Pot guard: a Pot not deployed yet must be where the deployed Kitchen’s predictPot puts it, and an owner of zero is refused. sauce run checks its whole plan before it sends the Pot’s deployment, the approvals and the transfers of --fund, so none of them is sent when the cook after them would be refused. sauce pot --offline prints the address unchecked, and says so.

The amount syntax

--fund and --assume-balance take TOKEN:amount. TOKEN is a registry symbol of the chain (matched without regard to case), a token address, or ETH (also native) for the chain’s native coin. A symbol the chain does not register is refused with the command that lists the chain’s tokens; give the token’s address instead. A fraction with n is refused:
A bare integer of base-unit size gets a note and is still read as whole tokens:
--assume-balance USDC:1500000n gives the same Pot 1.5 USDC.

Pull tokens in the cook: --fund

sauce run --fund TOKEN:amount wraps the program. The new main takes the same parameters, pulls each amount from your wallet into the Pot, calls the program’s own main with the arguments as given, and, with --sweep, sends what is left back to you. In a program with no main, the pulls go after its imports and the sweep after its last statement. --fund is repeatable, one token each. Before anything is sent, the run checks that your wallet holds each amount. Here docs is a throwaway wallet that holds no USDC:
Every pull is a safeTransferFrom: @sauce/token’s, or Permit2’s own. Signatures are made locally by the signer you gave. --fund signs and sends only for your own Pot: one the chain shows is yours (its owner you, its address the one a known release’s Kitchen gives your owner and salt), or the one the run deploys first. Any other --pot, or one whose owner the endpoint cannot read, is refused before anything is signed or sent, by sauce run and by sauce simulate given your key. A wallet with a policy checks two destinations. The funding is held to chains and to to with the Pot as the address. Each transaction the run sends before the cook is also held to to with its own destination, the token contract: the approve of --fund-mode allowance, the transfer of --fund-mode transfer, and the first approval of Permit2 under --fund-mode permit2. A policy whose to lists only the Pot refuses those three; adding the token’s address to to lets them through. permit, and permit2 once Permit2 is approved, send nothing before the cook and pass with the Pot alone.

--fund-mode

  • permit2 works for every ERC-20, USDT included, and keeps the run to one transaction. Each signature names one token, one amount and the Pot as spender, with a fresh nonce and a deadline ten minutes after the latest block.
  • permit needs a token that implements EIP-2612. The run reads the token’s domain and checks it against its DOMAIN_SEPARATOR().
  • allowance sets the Pot’s allowance from your wallet. A smaller standing allowance is reset to zero first, as OpenZeppelin’s forceApprove does. If the cook then fails, the allowance stands until a later cook spends it.
  • transfer sends the tokens to the Pot before the cook. If the cook then fails, they stay in the Pot.
allowance and transfer send a transaction before the cook, outside the cook’s atomic revert.

Testnets have no Permit2

The registries of Arc Testnet, Base Sepolia and Ethereum Sepolia have no Permit2 entry, so a token --fund in the default permit2 mode is refused before anything is signed, and the command exits 2:
Choose the mode yourself there: --fund-mode transfer for any ERC-20, allowance, or permit for a token that implements EIP-2612. With --fund-mode transfer the same command succeeds: simulated on Base Sepolia at block 50914243 with no key to sign the pull, the Pot is credited the 1 USDC the transfer would bring. Funding with the native coin alone, such as --fund ETH:0.001, rides on the cook’s value, needs no Permit2 and works in the default mode. sauce tx --fund plans plain transfers and needs no Permit2 either.

Permit2 and the first-time approval

Under permit2, Permit2 moves the tokens, so the first time you fund a token, Permit2 needs your approval for it. That approval is unlimited (maxUint256), not the amount you fund. The run asks before it sends it, as it asks before deploying a Pot; -y (--yes) approves without asking. The approval stands, as Permit2 is designed: each later pull still needs your signature for one amount, to one spender, once. With no terminal to ask on, or with --json, the run sends nothing and names the approval it needs, unless -y is given: -y approves every pre-cook transaction, including the unlimited Permit2 approval, with no terminal and under --json. sauce run lists every transaction it asks about. A wallet whose policy limits the token refuses that approval: approving Permit2 makes every Permit2 allowance of the token you gave before spendable again, which no policy can measure. From such a wallet, fund with --fund-mode permit, allowance or transfer. On a local fork of Base at block 53011315 with the current contracts, a wallet limited to 1 USDC ran --fund USDC:0.5 --yes in the default mode:
Nothing was sent. The same run with --fund-mode permit pulled the 0.5 USDC and succeeded.

--sweep

--sweep sends the Pot’s whole balance of each funded token back to the owner after the program, and of the native coin when ETH is funded. It returns everything the Pot holds of those tokens, not only what this run brought.

--fund in sauce tx

sauce tx --fund has no modes and no sweep. It adds a plain transfer from the owner to the Pot for each token, after the Pot’s deployment and before the cook. ETH:<amount> is added to the cook’s value. A wallet that sends the plan as one batch (sendCalls) deploys, funds and cooks with one approval.

Simulate with balances the Pot does not hold

--assume-balance TOKEN:amount sets the Pot’s balance for one sauce simulate run, by state override. It is repeatable, one token each, and ETH:<amount> sets the native balance. The command finds the slot that holds the balance by asking the token, then confirms it with balanceOf. A token whose balance is computed rather than stored (rebasing tokens, shares) is refused.
sauce simulate also takes --fund, --fund-mode and --sweep:
  • With the owner’s key (--wallet, --keystore or --private-key), it signs the pulls sauce run would make and sends nothing. An approval the run would ask for is reported and simulated before the cook.
  • With --owner and no key, the pulls cannot be signed. The Pot is credited what they would bring, --sweep still runs, and a shortfall in the owner’s wallet is noted rather than refused.
On Solana, --fund is not available. --assume-balance makes the simulation run in a local copy of the accounts, where the account the cook spends the token from is given the balance.

On Arc: USDC is the native coin

Arc’s native coin is USDC. The chain’s gas, a cook’s value and the Kitchen’s fee are paid in it, and it is the same balance as the USDC ERC-20: 18 decimals as native value, 6 through the token. The command treats the two as one: ETH on Arc never means ether. sauce pot gives the Kitchen’s fee in wei and in USDC:
In a program, pot.USDC reads the Pot’s 6-decimal USDC token balance on Arc, and pot.balance its 18-decimal native balance: the same money at the two scales.
pot-balance.sauce.ts
Simulated on Arc at block 27999763:
--assume-balance USDC:5 set the Pot’s native balance, which the token reports as 5000000 in its 6 decimals. With --json, the assumed balance is { "token": "native", "symbol": "USDC", "amount": "5000000000000000000" }, in 18 decimals. The cook’s value and the Kitchen’s fee move as native USDC and each is listed once. --assume-balance ETH:5 gives the same result. On Arc, sauce tx also notes on stderr that a transaction is included only with a maxFeePerGas of at least 20 gwei.

Next steps