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

# Pot

> The account your programs run in and your funds sit in, and who can act on it. Read it before you fund a Pot or let another account submit a cook.

On an EVM chain a private Pot is a contract the Kitchen creates for one owner. Every program runs inside a Pot: each call the program makes comes from the Pot, `pot.USDC` is the Pot's USDC balance, and what a call pays to its caller lands in the Pot. Only the owner can cook on it, or authorize a cook that another account submits ([Signed cooks](#signed-cooks)). The owner is fixed when the Pot is created. A program reads it as `pot.owner`, the address the Pot's `owner()` returns. A Shared Pot has no owner: there `pot.owner` is the zero address.

On Solana a Pot is an account the Kitchen derives and signs for. [A Pot on Solana](#a-pot-on-solana) covers the differences.

## Its address

A Pot's address follows from the Kitchen, the owner and a 32-byte salt. For a Pot that `deployPot(owner, salt)` creates, the kind the `sauce` command and the SDK's helpers use:

```text theme={null}
CREATE2(kitchen, keccak256(abi.encode(owner, salt)), potInitCodeHash)
```

A Pot whose Router is fixed, created with `deployPot(owner, salt, true)`, hashes `abi.encode(owner, salt, true)` instead, so the same owner and salt give a different address; a Shared Pot hashes `abi.encode(address(0), salt, true)`. The Kitchen's `predictPot(owner, salt, true)` and `predictSharedPot(salt)` return them. For the owner `0x1111…1111` and the default salt, on a local fork of Base at block 52992458 with the current contracts, `predictPot` returned `0x6c91…D3Fe` and `0xF993…7a27`, and the formula gives the same two addresses.

* On EVM, it is known before the Pot exists, as the prediction of the Kitchen the chain runs. Tokens sent to a predicted address that no Pot is deployed at are out of reach of every cook. Deploy the Pot before you send it funds, with `sauce pot --chain <chain> --deploy` and the owner's signer, or with `sauce run --fund`, which deploys the Pot before it funds it ([Funding a Pot](https://docs.eco.com/sauce-sdk/cli/funding.md)). `sauce tx --fund` plans the deployment first only when its endpoint shows the Pot has no code; with `--offline` it cannot check, so deploy first. Without `--offline`, `sauce pot` also reports whether the chain has the Kitchen, and refuses the deployment on a chain the `sauce` command does not send on.
* A deployed Pot's address never changes.
* The Kitchen has the same address on every EVM chain it is on, so where each chain runs the same Kitchen release, an owner's Pot under one salt has the same address on each of them.

```bash theme={null}
sauce pot --chain base --owner <address> --salt treasury --offline
sauce pot --chain ethereum --owner <address> --salt treasury --offline
```

For the owner `0x1111…1111`, both print the same Pot, `0x0828…Ec99`, under the salt `0xcbd8…d8ac`. `--offline` derives the address without reading a chain, so it is not checked against the deployed Kitchen, and the command says so.

### Salts

An owner has one Pot per salt.

* The default salt is zero: the owner's default Pot. In TypeScript it is `DEFAULT_POT_SALT`.
* `--salt <label>` names another Pot: its salt is the keccak256 of the label's UTF-8 bytes. In TypeScript, `potSalt(label)`.
* `--salt-hex <bytes32>` gives the salt exactly.

A salt is not a namespace. Whoever sends `deployPot(owner, salt)` creates the same Pot, owned by `owner`, and no one can create a different contract at that address.

```ts theme={null}
import { potSalt, v12PredictPot } from "@eco-incorp/sauce";
import type { Hex } from "viem";

export function potsOf(owner: Hex) {
  return {
    main: v12PredictPot(owner), // salt DEFAULT_POT_SALT, 32 zero bytes
    treasury: v12PredictPot(owner, potSalt("treasury")), // salt keccak256("treasury")
  };
}
```

`v12PredictPot` reads no chain.

## Deploying it

The Kitchen's `deployPot(owner, salt)` deploys a Pot ([Kitchen](https://docs.eco.com/sauce/kitchen.md#creating-a-pot)). Any account may send it, the Pot is the owner's whoever sends it, and a second send returns the Pot already there. It carries no fee. On a local fork of Base at block 52965000 with the current contracts, a simulated deployment used 667,003 gas.

You rarely send it yourself:

* `sauce run` deploys the Pot before the first cook that needs it, as a transaction of its own, after asking (or with `--yes`).
* `sauce tx` prints a plan when the Pot has no code yet: `deployPot` first, marked `deploysPot`, then the cook.
* `sauce simulate` deploys the Pot inside the simulation first.
* `sauce pot --deploy` sends the deployment alone.

## What it holds

On EVM, a deployed Pot is funded like any address: send it tokens or native coin from any wallet once its deployment has landed ([Its address](#its-address)), or let `sauce run --fund`, which deploys the Pot first, pull them from the wallet in the same transaction as the cook. Offline, `sauce tx --fund` cannot check deployment and adds no deployment step.

What a cook produces lands where the program sends it, and a call that credits its caller credits the Pot. A swap with no other recipient, a lending market's receipt tokens supplied for `pot` (Aave's aTokens with `onBehalfOf` the Pot), and vault shares minted to the caller all stay in the Pot. They are the owner's, but a wallet app does not show them, and only a later cook moves them.

To deliver a result to a wallet, the program names the wallet as the recipient or beneficiary, from a `main` parameter:

```ts supply.sauce.ts theme={null}
// Supplies `amount` of the Pot's USDC to Aave V3 for `onBehalfOf`, who receives the aUSDC.
//
//   sauce simulate supply.sauce.ts --chain base --owner <address> --assume-balance USDC:100 --args <onBehalfOf> 100000000
//
//   onBehalfOf: Address   who receives the aUSDC: your wallet, or the Pot itself to keep the position there
//   amount: Uint256       in USDC's smallest unit (6 decimals): 100000000 is 100 USDC
function main(onBehalfOf: Address, amount: Uint256) {
  USDC.approve(AaveV3.Pool, amount);
  AaveV3.Pool.supply(USDC, amount, onBehalfOf, 0);
}
```

Simulated on a local fork of Base at block 52965000 with the current contracts, with the owner's address as `onBehalfOf`, the aUSDC was minted to the owner's wallet and the 100 USDC left the Pot. Keeping a position in the Pot is a choice too: a later cook can withdraw it or borrow against it without the wallet moving anything back first.

## No withdraw function

A Pot has no function that withdraws or transfers what it holds. What a Pot holds leaves only through a cook: a program that sends it.

```ts sweep.sauce.ts theme={null}
// Sends all of the Pot's USDC to `to`.
//
//   sauce run sweep.sauce.ts --chain base --wallet <name> --args <to>
//
//   to: Address   the recipient, such as your wallet
function main(to: Address) {
  USDC.transfer(to, pot.USDC);
}
```

To give a Pot's holdings to someone else, cook a program that transfers them to that person or to their own Pot. The Pot itself cannot change hands.

## Who can do what

The table describes EVM Pots. On Solana the Kitchen's upgrade authority can replace the program that signs for every Pot, and must be trusted with what Pots hold ([Trust on Solana](https://docs.eco.com/sauce/kitchen.md#trust-on-solana)).

| | Can | Cannot |
| - | - | - |
| The Pot's owner | cook any program on it; authorize a cook that another account submits ([Signed cooks](#signed-cooks)); change its Router, unless the Router was fixed at creation | hand the Pot to another owner |
| Anyone else | deploy a Pot for any owner; send a Pot tokens or native coin; submit a cook the owner authorized for them | cook on it otherwise, or move anything it holds |
| The Kitchen's owner | set the fee, with no cap, so a fee no cook can pay stops every Pot's cooks ([Kitchen](https://docs.eco.com/sauce/kitchen.md#what-the-kitchens-owner-controls)); choose the Router that new Pots start on; upgrade the Kitchen | change a deployed Pot, its owner or its Router, or move what it holds |
| The engine | run a program inside a Pot its Kitchen created | run anywhere else: called directly, it refuses |

An EVM Pot's code never changes once it is deployed: nothing upgrades it.

## Signed cooks

A Pot created by the current Kitchen has a second way to run a program. The owner signs a cook off-chain, and any account submits it with `cookSigned`: a relayer that pays the gas, a service that sends it later, a second account of the owner's. The Pot runs it as it runs the owner's own `cook`, if and only if the owner signed that engine, that program with its arguments, that value, that fee ceiling, that nonce, that deadline and that submitter, for that Pot, on that chain.

```solidity theme={null}
function cookSigned(
    address engine,
    bytes program,
    uint256 maxExecutionFee,
    uint256 nonce,
    uint256 deadline,
    address submitter,
    bytes signature
) payable returns (bytes);

function invalidateNonces(uint256 word, uint256 mask);
```

**What is signed.** The owner signs an EIP-712 `Cook` under the domain `{ name: "Sauce Pot", version: "1", chainId, verifyingContract: <the Pot> }`, so one signature is good for one Pot on one chain:

| Field | What it binds |
| - | - |
| `engine` | The engine the Pot delegatecalls. It runs with the Pot's whole authority, so it is signed |
| `program` | The bytecode with `main`'s arguments appended: exactly what the engine runs |
| `value` | What the submitter's `msg.value` must be. Any other amount fails with `BadSignature()` |
| `maxExecutionFee` | The most of the Kitchen's fee the Pot pays for this cook |
| `nonce` | Which authorization this is: each nonce runs once |
| `deadline` | The last block timestamp at which it runs, inclusive; later it fails with `Expired()` |
| `submitter` | The only account that may submit it, or the zero address for anyone; another caller gets `WrongSubmitter()`. It is the Pot's immediate caller: through a forwarding contract, that contract |

Who submits when `submitter` is zero, when it is submitted up to the deadline, the gas and the transaction's place in its block are not signed. The program sees the submitter as `ctx.msgSender()`, so it can pay whoever submitted it.

**The fee.** A signed cook pays the Kitchen's fee once, as a cook does, but the Pot pays it from its own balance, which includes the call's value, and never more than `maxExecutionFee`. The Kitchen's owner sets the fee and may raise it after the owner signs. A fee above the smaller of the Pot's balance and the ceiling fails with `FeeNotPaid`. The submitter sends exactly the signed value and pays the gas.

**Nonces.** Nonces are unordered: a used nonce sets bit `nonce & 255` of word `nonce >> 8` in the Pot's storage, in any order, before the program runs. A second submission fails with `NonceUsed()`. A cook that fails reverts everything, its nonce included, so the same signature can be submitted again until its deadline. `invalidateNonces(word, mask)` sets bits to cancel authorizations not yet used, and emits `NoncesInvalidated(uint256 indexed word, uint256 mask)`. The owner can call it, and so can the Pot itself, so a signed cook can cancel others. A signed cook emits `SignedCook(uint256 indexed nonce, address indexed submitter)`.

**Whose signature counts.** The owner's: a 65-byte signature that `ecrecover` recovers to the owner, or, when the owner has code, whatever the owner's ERC-1271 `isValidSignature` accepts. A Safe or an EIP-7702 account signs this way. Anything else fails with `BadSignature()`.

**Which Pots take it.** Private Pots created by the current Kitchen, with a fixed Router or not. A Shared Pot has no owner to sign and refuses every signed cook.

The SDK builds each part: [`sauce sign`](https://docs.eco.com/sauce-sdk/cli/sign) signs a cook with a wallet or prints the typed data for one kept elsewhere, and cancels one; the MCP server has [`sauce_signed_cook`](https://docs.eco.com/sauce-sdk/mcp/tools#sauce_signed_cook) and, over stdio, `sauce_sign`; and TypeScript has `signedCookTypedData`, `encodeCookSigned`, `encodeInvalidateNonces`, `verifySignedCook` and `checkSignedCookPot` from `@eco-incorp/sauce/evm/engine` ([Cook transactions](https://docs.eco.com/sauce-sdk/typescript/cook-transactions.md#signed-cooks)). [Security considerations](https://docs.eco.com/sauce/security-considerations.md#signed-cooks) covers what to watch for.

## The Pot's interface

`v12PotAbi` from `@eco-incorp/sauce/evm/engine` is the complete ABI. The parts a developer uses:

| Member | What it is |
| - | - |
| `cook(address engine, bytes program) payable returns (bytes)` | runs one program on `engine`; owner only ([cook()](https://docs.eco.com/sauce/cook.md)) |
| `cookSigned(…)` | runs one program the owner authorized, submitted by another account; Pots of the current Kitchen only ([Signed cooks](#signed-cooks)) |
| `invalidateNonces(uint256 word, uint256 mask)` | cancels signed-cook authorizations; emits `NoncesInvalidated(word, mask)`; Pots of the current Kitchen only |
| `owner()`, `kitchen()`, `userSalt()` | the owner, the Kitchen that created the Pot, and the salt it was created under |
| `router()` | the Pot's Router |
| `setRouter(address newRouter)` | points the Pot at another Router; owner only; emits `RouterSet(router)` |
| `inFlightEngine()` | the engine of the cook now running, or the zero address outside one |
| `NotOwner()` | the error for a `cook` or `setRouter` from anyone but the owner |
| `FeeNotPaid(uint256 required, uint256 provided)` | the error for a cook whose value is below the Kitchen's fee |

The ABI also declares the event `SignedCook(nonce, submitter)` and the errors `BadSignature()`, `Expired()`, `NonceUsed()`, `WrongSubmitter()`, `EmptyProgram()`, `EngineHasNoCode()`, `FeeTransferFailed()`, `NotInFlightEngine()`, `RouterFixed()`, `RouterHasNoCode()`, `RouterUnset()` and `SharedRouterNotFixed()`. `sauce simulate` prints a Pot's or the Kitchen's revert by its error's name and arguments, such as `FeeNotPaid(1234, 0)`.

## The Pot's Router

Some pools call their caller back in the middle of a swap to be paid: Uniswap V3 and its forks, Uniswap V4, Maverick V2 and PancakeSwap Infinity. A program cannot answer a callback. The Pot answers it: a call the Pot does not recognize goes to its fallback, which delegatecalls the Pot's Router, and the Router pays the pool from the Pot exactly what the pool asks for, up to the swap's input amount. The `@sauce/router` functions for those venues call the Router through the Pot.

Venues that are paid first, Uniswap V2, Curve, Balancer V2, DODO V2, Trader Joe LB and WOOFi, are called directly by the program and do not use the Router.

* A new Pot starts on the Router the Kitchen names in `router()` when the Pot is created.
* After that, only the Pot's owner can change it, with the Pot's `setRouter`. A call from anyone else reverts with `NotOwner()`.
* A Pot created with `deployPot(owner, salt, true)` keeps its Router for good, and so does every Shared Pot.
* The Kitchen's owner changes the Router that Pots created afterwards start on, and nothing else. A Router change moves no Pot address.

The Router is content-addressed: its address follows from its code, so a Router release with other code has another address. `v12RouterAddress()` from `@eco-incorp/sauce/deployments` returns the current Router; compare a Pot's `router()` with it by address.

After a new Router is released, `potRouterUpdate(client, pot)` from `@eco-incorp/sauce/evm/engine` reads the Pot's Router and the Kitchen's through your client. It returns the `setRouter` call for the owner to sign, or nothing when the Pot is current. It sends nothing, and it throws for a Pot that the current Kitchen did not create.

## A Pot on Solana

* **Address.** A Pot is a program-derived address of the Kitchen program, from the seeds `"pot"`, the owner and a 32-byte salt. It is known before the Pot exists, and the default salt is zero, as on EVM chains.
* **Creation.** The Kitchen's `create_pot` creates it. The owner signs that instruction and pays the rent, Solana's deposit for storing an account. `sauce run` asks before creating it, and `sauce tx` puts it in the setup it prints.
* **Storage.** A Pot keeps 10,240 bytes of storage for programs, fixed when it is created. In a simulation in the `sauce` command's local copy (LiteSVM) of the cluster at slot 460261665, its rent was 52,669,440 lamports, and the rent of the Pot's USDC token account 1,488,440 lamports.
* **Cook instructions.** The Kitchen's `cook` and `cook_from_account` carry the Pot's salt and then its bump, the byte that makes the Pot's address a valid program-derived address. The SDK's builders and the `sauce` command derive the bump. An instruction serialized for the Kitchen before the current one has the earlier format and names the earlier engine: build cooks with the installed SDK.
* **Signing.** The current Kitchen requires the owner to sign each cook, and signs for the Pot, so the program can spend tokens whose authority is the Pot. The Kitchen program is upgradeable: its upgrade authority can replace that check, and must be trusted with what the Pot holds ([Trust on Solana](https://docs.eco.com/sauce/kitchen.md#trust-on-solana)).
* **Token balances.** The Pot's tokens sit in its token accounts, one per mint, at the associated token account under the mint's token program. `sauce pot --chain solana` lists them.
* **`pot` and `ctx.self()`.** In a program on Solana, `ctx.self()` is the engine program's id, not the Pot. `pot` is the Pot: its key, its signer slot where an `Account` is taken, and the holder in `pot.USDC`. `pot.owner` is the owner's account; `pot.owner.writable().signer()` gives it both roles. `Account(pot.address).owner` reads the program that owns the Pot account instead.
* **No Pot.** A cook sent directly to the engine runs with no Pot, and has only the signers its transaction gives it. A program that uses `pot`, `pot.balance` or `pot.<TOKEN>` is refused there; one that reads only `pot.owner` is not.

```bash theme={null}
sauce pot --chain solana --owner <public key>
```

## Next steps

* [cook()](https://docs.eco.com/sauce/cook.md): the call that runs a program on a Pot.
* [Kitchen](https://docs.eco.com/sauce/kitchen.md): who creates Pots and sets the fee.
* [Security considerations](https://docs.eco.com/sauce/security-considerations.md): what to check before you sign a cook.
* [`sauce pot`](https://docs.eco.com/sauce-sdk/cli/pot): reading and deploying a Pot.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.