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

# Swap on one pool, fall back to another

> One Base transaction that sells WETH on the Uniswap V3 0.05% pool inside an atomic try block and, when that attempt fails, on an Aerodrome Slipstream pool.

The program sells `amount` WETH for USDC, paid to `recipient`. Venue A, Uniswap V3's WETH/USDC 0.05% pool with a price limit, runs inside a `try` block with two checks: at least `minOut` USDC received, and all of `amount` sold. When any statement in the block fails, the block is undone and the `catch` sells on venue B, the Slipstream pool that `AerodromeSlipstream.CLFactory` keeps at `tickSpacing`. The cook reverts when B also delivers less than `minOut` or does not sell all of `amount`.

**Target chains:** Base. The program requires Aerodrome Slipstream. Compiling for Ethereum fails (`` `AerodromeSlipstream.CLFactory` is not deployed on ethereum. ``).

## Prerequisites

Install the [Sauce CLI](https://docs.eco.com/sauce-sdk/installation.md) to compile and simulate. To send the transaction, configure a wallet with funds for gas and the Kitchen fee, and provide WETH in your Pot once it is deployed, or through [`--fund`](https://docs.eco.com/sauce-sdk/cli/funding).

## Program

`pot` is the Pot the program runs on, available without declaring it ([Names](https://docs.eco.com/sauce/names.md#balances)).

```ts try-fallback-swap.sauce.ts theme={null}
// Sells `amount` of the Pot's WETH for USDC on Base, paid to `recipient`. Venue A is Uniswap V3's
// WETH/USDC 0.05% pool, stopped at `sqrtPriceLimitX96`. When A fails, or does not sell all of
// `amount` for at least `minOut`, venue B sells it instead: the Aerodrome Slipstream WETH/USDC
// pool that AerodromeSlipstream.CLFactory keeps at `tickSpacing`. A runs in an atomic try block,
// so its swap, transfers and assignments are undone before B runs. Returns the venue that sold
// (1 for A, 2 for B) and the USDC received. The cook reverts, and nothing moves, when B also
// delivers less than `minOut` or does not sell all of `amount`.
//
//   sauce simulate try-fallback-swap.sauce.ts --chain base --owner <address> \
//     --assume-balance WETH:<amount>n \
//     --args <amount> <sqrtPriceLimitX96> <minOut> <tickSpacing> <recipient>
//   sauce run try-fallback-swap.sauce.ts --chain base --wallet <name> --fund WETH:<amount>n \
//     --args <amount> <sqrtPriceLimitX96> <minOut> <tickSpacing> <recipient>
//
//   amount: Uint256             the WETH to sell, in wei (18 decimals): 1000000000000000000 is
//                               1 WETH; the Pot must hold it
//   sqrtPriceLimitX96: Uint256  venue A's price limit, a floor, since selling WETH (token0 on
//                               Base) lowers the price; `sauce price --chain base WETH USDC <p>`
//                               prints it for a price p in USDC per WETH
//   minOut: Uint256             the least USDC `recipient` may receive, in USDC base units
//                               (6 decimals): 2320000000 is 2,320 USDC
//   tickSpacing: Uint256        venue B's tick spacing on AerodromeSlipstream.CLFactory: 100
//   recipient: Address          receives the USDC
//
// On Base, sell 1 WETH for at least 2,320 USDC, venue A no lower than 2,320 USDC per WETH
// (sqrtPriceLimitX96 3816133648877761641136391), venue B at tick spacing 100, for <recipient>:
//
//   sauce run try-fallback-swap.sauce.ts --chain base --wallet me --fund WETH:1 \
//     --args 1000000000000000000 3816133648877761641136391 2320000000 100 \
//     <recipient>
import { uniswapV3 } from "@sauce/router";

function main(
  amount: Uint256,
  sqrtPriceLimitX96: Uint256,
  minOut: Uint256,
  tickSpacing: Uint256,
  recipient: Address,
): { venue: Uint256; received: Uint256 } {
  require(amount > 0, "amount is 0");
  require(recipient != 0, "no recipient");
  const wethBefore = pot.WETH;
  require(wethBefore >= amount, "not enough WETH in the Pot");
  const poolA = UniswapV3.Factory.getPool(WETH, USDC, 500);
  const poolB = AerodromeSlipstream.CLFactory.getPool(WETH, USDC, tickSpacing);
  require(poolA != 0, "no venue A pool");
  require(poolB != 0, "no venue B pool at tickSpacing");
  const usdcBefore = recipient.USDC;

  // Venue A: the swap and both checks commit together or not at all. When any of them fails,
  // `venue = 1` is undone with the swap, so the handler and the return see 2.
  let venue = 2;
  try {
    venue = 1;
    uniswapV3(poolA, WETH, USDC, amount, sqrtPriceLimitX96, 0, recipient);
    require(recipient.USDC - usdcBefore >= minOut, "venue A below minOut");
    require(pot.WETH == wethBefore - amount, "venue A did not sell all of amount");
  } catch {
    // Venue B, with no price limit: minOut bounds the price of all of `amount`. A failure here is
    // not caught, so it reverts the whole cook.
    uniswapV3(poolB, WETH, USDC, amount, 0, 0, recipient);
    require(recipient.USDC - usdcBefore >= minOut, "venue B below minOut");
    require(pot.WETH == wethBefore - amount, "venue B did not sell all of amount");
  }
  const received = recipient.USDC - usdcBefore;
  return { venue, received };
}
```

## How it works

* The `try` block holds a swap and two `require`s, so it is atomic: when any of them fails, the block's calls, transfers, storage writes and assignments are undone before the handler runs. That undoes A's partial fill in the second simulation below, and `venue = 1` with it, so `venue` is still 2 without the handler assigning it. Work before the block stays: the pool lookups and `usdcBefore`.
* A `try` around one external call alone is that call's `.catch()`: it catches the call's failure, not its argument evaluation or the decoding of a successful return. The checks sit inside this block, which makes a short or partial fill a failure the handler sees.
* The handler itself is not protected: a failure in it reverts the whole cook. `catch { … }` discards the revert data; `catch (error)` would receive at most its first 256 bytes. There is no `finally`, and `try`/`catch` is EVM only: Solana has no catch.
* Venue B has no price limit (`0`), so `minOut` is the only bound on the price it gives for all of `amount`. WETH is token0 on Base, so venue A's `sqrtPriceLimitX96` is a floor: selling WETH lowers the pool's price.

## Run the example

**1. Set venue A's limit**

```bash theme={null}
sauce price --chain base WETH USDC 2320
```

Its "Selling WETH" block gives the `sqrtPriceLimitX96` for 2,320 USDC per WETH, `3816133648877761641136391`, a floor.

**2. Compile**

```bash theme={null}
sauce compile try-fallback-swap.sauce.ts --chain base
```

**3. Simulate**

```bash theme={null}
sauce simulate try-fallback-swap.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:1 \
  --args 1000000000000000000 3816133648877761641136391 2320000000 100 <recipient>
```

`--assume-balance WETH:1` gives the Pot 1 WETH for this simulation only. The result lists the venue that sold and the USDC received.

**4. Send**

```bash theme={null}
sauce run try-fallback-swap.sauce.ts --chain base --wallet <name> --fund WETH:1 \
  --args 1000000000000000000 3816133648877761641136391 2320000000 100 <recipient>
```

`sauce tx` prints the unsigned transaction instead, for your own signer, with the same funding and arguments:

```bash theme={null}
sauce tx try-fallback-swap.sauce.ts --chain base --owner <your-address> --fund WETH:1 \
  --args 1000000000000000000 3816133648877761641136391 2320000000 100 <recipient>
```

## Simulation result

Simulated on a local fork of Base at block 53122981 with the current contracts. Nothing was signed or sent; values change with the block.

```text theme={null}
The cook succeeds from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: gas used 245308 for the cook, after the Pot's deploy (1. below), returns 0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000008ae5fdfd
main returns { venue: Uint256, received: Uint256 }: { venue: 1, received: 2330328573 }
...
What moved, in order:
...
  [3] 0xd0b5…F224        -> 0x7099…79C8          2330.328573 USDC
  [4] Pot                -> 0xd0b5…F224          1 WETH
Net balance changes:
  0xd0b53D9277642d899DF5C87A3966A349A798F224: -2330.328573 USDC, +1 WETH
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8: +2330.328573 USDC
  0xDcFCc75A3C9a0EC798CAf81B6C84D4c5d5988c49 (the Pot): -1 WETH
  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (owner): -0.00000374478538635 ETH
...
```

Venue A committed (`venue: 1`): the 0.05% pool (0xd0b5…F224) took the 1 WETH and paid 2,330.328573 USDC to the recipient.

On the same fork, with venue A's floor at 2,331.50 USDC per WETH (`3825580047006472742861822`), under the 0.05% pool's price, which `sauce pools` listed at 2,331.62 USDC per WETH before its fee:

```bash theme={null}
sauce simulate try-fallback-swap.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:1 \
  --args 1000000000000000000 3825580047006472742861822 2320000000 100 <recipient>
```

```text theme={null}
The cook succeeds from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: gas used 398365 for the cook, after the Pot's deploy (1. below), returns 0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000008aea994b
main returns { venue: Uint256, received: Uint256 }: { venue: 2, received: 2330630475 }
...
What moved, in order:
...
  [3] 0xb2cc…DC59        -> 0x7099…79C8          2330.630475 USDC
  [4] Pot                -> 0xb2cc…DC59          1 WETH
Net balance changes:
  0xb2cc224c1c9feE385f8ad6a55b4d94E92359DC59: -2330.630475 USDC, +1 WETH
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8: +2330.630475 USDC
  0xDcFCc75A3C9a0EC798CAf81B6C84D4c5d5988c49 (the Pot): -1 WETH
  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (owner): -0.00000374478538635 ETH
...
```

Simulated alone with that floor, venue A sells 0.477530653714293159 WETH for 1,112.834849 USDC and stops, below `minOut` and short of `amount`. Inside the `try` block that fill was undone: the output lists no transfer with the 0.05% pool, `venue` is 2, and venue B (0xb2cc…DC59) sold the whole 1 WETH for 2,330.630475 USDC.

On the same fork, with `minOut` at 2,350 USDC, both venues are short and the cook reverts:

```bash theme={null}
sauce simulate try-fallback-swap.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:1 \
  --args 1000000000000000000 3816133648877761641136391 2350000000 100 <recipient> \
  --expect-revert "venue B below minOut"
```

```text theme={null}
As expected, the cook reverts: the program threw "venue B below minOut"
The cook reverts: the program threw "venue B below minOut"
...
```

## Next steps

* [Multi-venue route](https://docs.eco.com/quickstarts/sauce/multi-venue-route.md): two venues in sequence, each leg with its own minimum.
* [Distributed swap](https://docs.eco.com/quickstarts/sauce/distributed-swap.md): one order across many pools, each stopped at its own limit.
* [Built-ins](https://docs.eco.com/sauce/builtins.md): `try`/`catch` and `.catch()`, [`sauce price`](https://docs.eco.com/sauce-sdk/cli/price), [`sauce simulate`](https://docs.eco.com/sauce-sdk/cli/simulate)


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