> ## 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.

# Sauce by example

> Five annotated SauceScript programs, each adding one idea to the last, for learning the language from code that compiles for Base and Ethereum.

These five examples build from a payment to a program that compares swap quotes and supplies the output to a lending protocol. Read them in order to learn the language, or use the [Sauce SDK quickstarts](https://docs.eco.com/quickstarts/sauce/overview.md) for complete task-specific recipes.

## Prerequisites

Read the [introduction](https://docs.eco.com/sauce/introduction.md) for the roles of a program, Pot and cook. To run the examples, complete the [SDK quickstart](https://docs.eco.com/quickstarts/sauce/first-program.md). The examples use SauceScript globals such as `USDC` and `pot`; only library functions need imports.

The programs target Base and Ethereum. The recorded simulations used a local fork of Base at block 53122981 with the documented release's contracts and an owner without a Pot. The simulation created a Pot before running each cook. Output is abridged, addresses are shortened and results can differ at another block. These results are simulation evidence, not a record of live transactions.

| Step | You learn to… |
| - | - |
| [Pay several recipients](#a-payment-to-several-recipients) | Use parameters, a balance check and a loop. |
| [Read a pool](#reading-a-pool) | Call a protocol and return a value. |
| [Compare two pools](#comparing-two-pools) | Add a helper, a record type and a conditional. |
| [Swap on the better pool](#swapping-on-the-better-pool) | Import a library and enforce a minimum output. |
| [Supply the output to Aave](#supplying-the-output-to-aave) | Use one action's output in the next. |

## A payment to several recipients

```ts payout.sauce.ts theme={null}
// Pays each of `recipients` `amount` of the chain's USDC, from the Pot.
//
//   sauce run payout.sauce.ts --chain base --wallet <name> --args <recipients> <amount>
//
//   recipients: Address[]  a JSON array: '["<recipient>","<recipient>"]'
//   amount: Uint256        what each recipient receives, in USDC's smallest unit (6 decimals)
function main(recipients: Address[], amount: Uint256) {
  require(pot.USDC >= amount * recipients.length, "not enough USDC");
  for (const to of recipients) {
    USDC.transfer(to, amount);
  }
}
```

* `recipients` is an array parameter. The CLI takes it as JSON and encodes it after the bytecode with `amount`.
* `amount * recipients.length` is checked arithmetic: an overflow reverts the transaction instead of wrapping.
* `require` runs before any transfer. If the Pot holds too little, nothing is paid.
* `for...of` iterates over an array. Each `USDC.transfer` is a separate token call from the Pot.

```bash theme={null}
sauce simulate payout.sauce.ts --chain base --owner <address> --assume-balance USDC:10 \
  --args '["<recipient>","<recipient>"]' 5000000
```

```text theme={null}
The cook succeeds from 0x1111…1111: gas used 118189 for the cook, after the Pot's deploy (1. below), returns 0x
What moved, in order:
  [1] wallet 0x1111…1111 -> Pot 0x6c91…D3Fe      0.00000374478538635 ETH  the cook's value
  [2] Pot                -> Kitchen 0x1984…506b  0.00000374478538635 ETH  the Kitchen's fee
  [3] Pot                -> 0x2222…2222          5 USDC
  [4] Pot                -> 0x3333…3333          5 USDC
```

This program does not compile for Solana: a token transfer there needs the recipient's token account listed in the transaction, and `to` is known only inside the loop. [EVM and Solana](https://docs.eco.com/sauce/targets.md) shows how a Solana program takes such accounts.

## Reading a pool

A program can read a protocol and return a value. Running it with `sauce simulate` gives you the result without sending a transaction.

```ts quote.sauce.ts theme={null}
// The USDC that `amountIn` WETH buys in the chain's Uniswap V3 WETH/USDC pool at the 0.05% tier.
//
//   sauce simulate quote.sauce.ts --chain base --owner <address> --args <amountIn>
//
//   amountIn: Uint256   in WETH's smallest unit (18 decimals): 10000000000000000 is 0.01 WETH
function main(amountIn: Uint256): Uint256 {
  const [amountOut] = UniswapV3.QuoterV2.quoteExactInputSingle({
    tokenIn: WETH,
    tokenOut: USDC,
    amountIn: amountIn,
    fee: 500,
    sqrtPriceLimitX96: 0,
  });
  return amountOut;
}
```

* `UniswapV3.QuoterV2` is the chain's deployment of Uniswap's QuoterV2. The registry supplies its address and ABI, so the call is typed: its struct parameter is written as a record, and a misspelled or missing field fails the compile.
* `quoteExactInputSingle` returns four values. `const [amountOut] = …` keeps the first.
* A Uniswap V3 fee tier is in millionths: `500` is 0.05%, `3000` is 0.3%.
* `main` declares `Uint256` as its return type, so the cook returns one 32-byte word and the CLI decodes it.

```text theme={null}
main returns Uint256: 23304536
```

With `--args 10000000000000000`, 0.01 WETH is quoted at 23.304536 USDC.

## Comparing two pools

```ts best-tier.sauce.ts theme={null}
// The better of the chain's two Uniswap V3 WETH/USDC pools, 0.05% and 0.3%, for selling
// `amountIn` WETH, and the USDC it quotes.
//
//   sauce simulate best-tier.sauce.ts --chain base --owner <address> --args <amountIn>
//
//   amountIn: Uint256   in WETH's smallest unit (18 decimals)
interface Quote {
  fee: Uint256;
  amountOut: Uint256;
}

function quote(amountIn: Uint256, fee: Uint256): Uint256 {
  const [amountOut] = UniswapV3.QuoterV2.quoteExactInputSingle({
    tokenIn: WETH,
    tokenOut: USDC,
    amountIn: amountIn,
    fee: fee,
    sqrtPriceLimitX96: 0,
  });
  return amountOut;
}

function main(amountIn: Uint256): Quote {
  const at500 = quote(amountIn, 500);
  const at3000 = quote(amountIn, 3000);
  if (at500 >= at3000) {
    return { fee: 500, amountOut: at500 };
  }
  return { fee: 3000, amountOut: at3000 };
}
```

* `quote` is a helper: a top-level function with annotated parameters and return type. It exists because the program quotes twice.
* `interface Quote` declares a record type. `main` returns one, and the cook returns its fields as one word each.
* Both quotes read the pools as they are when the transaction executes, not when it was built.

```text theme={null}
main returns { fee: Uint256, amountOut: Uint256 }: { fee: 500, amountOut: 23304536 }
```

## Swapping on the better pool

```ts swap-best.sauce.ts theme={null}
// Sells `amountIn` of the Pot's WETH for USDC in whichever Uniswap V3 WETH/USDC pool, 0.05% or
// 0.3%, quotes more, and pays the USDC to `recipient`.
//
//   sauce run swap-best.sauce.ts --chain base --wallet <name> --fund WETH:0.01 \
//     --args <amountIn> <minOut> <recipient>
//
//   amountIn: Uint256    WETH to sell, in its smallest unit (18 decimals)
//   minOut: Uint256      the least USDC to accept, in its smallest unit (6 decimals)
//   recipient: Address   who receives the USDC
import { uniswapV3 } from "@sauce/router";

function quote(amountIn: Uint256, fee: Uint256): Uint256 {
  const [amountOut] = UniswapV3.QuoterV2.quoteExactInputSingle({
    tokenIn: WETH,
    tokenOut: USDC,
    amountIn: amountIn,
    fee: fee,
    sqrtPriceLimitX96: 0,
  });
  return amountOut;
}

function main(amountIn: Uint256, minOut: Uint256, recipient: Address): Uint256 {
  const at500 = quote(amountIn, 500);
  const at3000 = quote(amountIn, 3000);
  const fee = at500 >= at3000 ? 500 : 3000;
  const pool = UniswapV3.Factory.getPool(WETH, USDC, fee);
  return uniswapV3(pool, WETH, USDC, amountIn, 0, minOut, recipient);
}
```

* `@sauce/router` is the SDK's swap library. `uniswapV3` takes the pool, the input and output tokens, the input amount, a price limit, the minimum output and the recipient, and returns what the recipient received.
* `minOut` is the minimum accepted output, supplied as a `main` parameter. If the swap returns less, `uniswapV3` reverts; because this program does not catch the failure, the whole transaction reverts.
* The price limit `0` sets none, so `minOut` is the only bound on the price. [Security considerations](https://docs.eco.com/sauce/security-considerations.md) explains when to set one.
* `UniswapV3.Factory.getPool` reads the pool's address for the chosen fee tier.
* A Uniswap V3 pool pays out first and then calls its caller back to be paid. The Pot's Router answers that callback and pays the pool from the Pot. [`@sauce/router`](https://docs.eco.com/sauce-libraries/router#how-each-venue-is-reached) lists which venues work this way.

```bash theme={null}
sauce simulate swap-best.sauce.ts --chain base --owner <address> --assume-balance WETH:0.01 \
  --args 10000000000000000 23000000 <recipient>
```

```text theme={null}
The cook succeeds from 0x1111…1111: gas used 355118 for the cook, after the Pot's deploy (1. below), returns 0x…01639958
main returns Uint256: 23304536
What moved, in order:
  [1] wallet 0x1111…1111 -> Pot 0x6c91…D3Fe      0.00000374478538635 ETH  the cook's value
  [2] Pot                -> Kitchen 0x1984…506b  0.00000374478538635 ETH  the Kitchen's fee
  [3] 0xd0b5…F224        -> 0x3333…3333          23.304536 USDC
  [4] Pot                -> 0xd0b5…F224          0.01 WETH
```

In this simulation, the swap delivered 23304536, matching the 0.05% pool’s quote. The program obtained the quote and performed the swap within the same transaction; `minOut` remains the explicit lower bound on the output.

## Supplying the output to Aave

```ts swap-and-supply.sauce.ts theme={null}
// Sells `amountIn` of the Pot's WETH for USDC in the better of the chain's two Uniswap V3
// WETH/USDC pools, then supplies all the USDC it received to Aave V3 for `beneficiary`.
//
//   sauce run swap-and-supply.sauce.ts --chain base --wallet <name> --fund WETH:0.01 \
//     --args <amountIn> <minOut> <beneficiary>
//
//   amountIn: Uint256      WETH to sell, in its smallest unit (18 decimals)
//   minOut: Uint256        the least USDC to accept, in its smallest unit (6 decimals)
//   beneficiary: Address   who receives the aTokens: a wallet, or the Pot to keep them there
import { uniswapV3 } from "@sauce/router";

function quote(amountIn: Uint256, fee: Uint256): Uint256 {
  const [amountOut] = UniswapV3.QuoterV2.quoteExactInputSingle({
    tokenIn: WETH,
    tokenOut: USDC,
    amountIn: amountIn,
    fee: fee,
    sqrtPriceLimitX96: 0,
  });
  return amountOut;
}

function main(amountIn: Uint256, minOut: Uint256, beneficiary: Address): Uint256 {
  const at500 = quote(amountIn, 500);
  const at3000 = quote(amountIn, 3000);
  const fee = at500 >= at3000 ? 500 : 3000;
  require((fee == 500 ? at500 : at3000) >= minOut, "both quotes below minOut");

  const pool = UniswapV3.Factory.getPool(WETH, USDC, fee);
  const received = uniswapV3(pool, WETH, USDC, amountIn, 0, minOut, pot);

  USDC.approve(AaveV3.Pool, received);
  AaveV3.Pool.supply(USDC, received, beneficiary, 0);
  return received;
}
```

* The swap's recipient is `pot`, the Pot, so the USDC stays in the Pot for the next call.
* `received` is what the swap returned, the amount the pool reports it paid. The program approves and supplies exactly that amount, so no allowance is left standing after the supply.
* `USDC.approve` resets a non-zero allowance to zero before setting the new one, as OpenZeppelin's `forceApprove` does.
* `AaveV3.Pool.supply(asset, amount, onBehalfOf, referralCode)` is Aave's own function, called by name. Aave mints the aTokens to `beneficiary`. With `pot` instead, they would stay in the Pot, and only a later cook could move them.
* The `require` fails early with a readable reason. `uniswapV3` enforces the same floor on the amount the pool reports it paid.

```bash theme={null}
sauce simulate swap-and-supply.sauce.ts --chain base --owner <address> --assume-balance WETH:0.01 \
  --args 10000000000000000 23000000 <beneficiary>
```

```text theme={null}
The cook succeeds from 0x1111…1111: gas used 552501 for the cook, after the Pot's deploy (1. below), returns 0x…01639958
main returns Uint256: 23304536
What moved, in order:
  [1] wallet 0x1111…1111 -> Pot 0x6c91…D3Fe       0.00000374478538635 ETH  the cook's value
  [2] Pot                -> Kitchen 0x1984…506b   0.00000374478538635 ETH  the Kitchen's fee
  [3] 0xd0b5…F224        -> Pot                   23.304536 USDC
  [4] Pot                -> 0xd0b5…F224           0.01 WETH
  [5] Pot                -> aBasUSDC 0x4e65…c0AB  23.304536 USDC
  [6] mint               -> 0x3333…3333           23.304534 aBasUSDC
Approvals:
  0x6c91…D3Fe (the Pot) lets 0xA238…d1c5 (AaveV3.Pool) spend 23.304536 USDC
```

The "Approvals" line is the approval the program set before the supply; Aave's `supply` then pulled the full 23.304536 USDC.

With `minOut` at 30 USDC, above both quotes, the same simulation reverts and nothing moves:

```bash theme={null}
sauce simulate swap-and-supply.sauce.ts --chain base --owner <address> --assume-balance WETH:0.01 \
  --args 10000000000000000 30000000 <beneficiary> --expect-revert "below minOut"
```

```text theme={null}
As expected, the cook reverts: the program threw "both quotes below minOut"
```

## What these programs used

| Feature | Reference |
| - | - |
| `main`, parameters, return values, helpers, `interface` | [Layout of a program](https://docs.eco.com/sauce/program-layout.md), [Functions](https://docs.eco.com/sauce/functions.md), [Types](https://docs.eco.com/sauce/types.md) |
| `for...of`, the conditional operator, checked arithmetic | [Expressions and control flow](https://docs.eco.com/sauce/expressions-and-control-flow.md) |
| `require` and reverts | [Errors and reverts](https://docs.eco.com/sauce/errors.md) |
| `USDC`, `pot.USDC`, `UniswapV3.QuoterV2`, `AaveV3.Pool` | [Names](https://docs.eco.com/sauce/names.md), [Contracts and ABIs](https://docs.eco.com/sauce/contracts-and-abis.md) |
| `uniswapV3`, `USDC.transfer`, `USDC.approve` | [`@sauce/router`](https://docs.eco.com/sauce-libraries/router), [`@sauce/token`](https://docs.eco.com/sauce-libraries/token) |
| `pot` | [Names](https://docs.eco.com/sauce/names.md), [Pot](https://docs.eco.com/sauce/pot.md) |
| The cook, its fee and its gas | [cook()](https://docs.eco.com/sauce/cook.md) |

## Next steps

* [Layout of a program](https://docs.eco.com/sauce/program-layout.md): the rules behind the programs above.
* [Security considerations](https://docs.eco.com/sauce/security-considerations.md): price limits, minimum outputs and approvals.
* [Sauce SDK quickstarts](https://docs.eco.com/quickstarts/sauce/overview.md): more programs, by task.


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