> ## 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 WETH, then pay a USDC invoice

> One Base transaction that sells WETH for USDC on Uniswap V3's 0.05% pool, pays an exact USDC invoice, and returns the extra USDC and any unsold WETH.

The program sells `amountIn` of the Pot's WETH for USDC on Uniswap V3's 0.05% WETH/USDC pool, pays exactly `due` USDC to `payee`, and sends the USDC above `due` and any WETH the pool did not take to `refundTo`. It reverts when the swap returns less than `due` USDC, so the invoice is paid in full or not at all.

**Target chains:** Base. The recorded simulation uses a Base fork.

> [!NOTE]
>
> With a floor set, the swap can stop before it sells all of `amountIn`. The invoice is still paid only when the USDC received covers `due`; the unsold WETH goes to `refundTo`, not back to the Pot.

## 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. Provide the required tokens in your Pot once it is deployed, or through `--fund`; see [funding](https://docs.eco.com/sauce-sdk/cli/funding.md).

## Program

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

```ts swap-then-pay.sauce.ts theme={null}
// Settles an invoice priced in USDC from the Pot's WETH: sells `amountIn` WETH on Uniswap V3's
// 0.05% WETH/USDC pool, down to the price floor `sqrtPriceLimitX96`, pays exactly `due` USDC to
// `payee`, and sends the USDC above `due` and any WETH the pool did not take to `refundTo`. The
// cook reverts, and nothing moves, when `amountIn` or `due` is zero, `payee` or `refundTo` is the
// zero address, the Pot holds less than `amountIn` WETH, the swap returns less than `due` USDC,
// or the payee receives a different amount.
//
//   sauce simulate swap-then-pay.sauce.ts --chain base --owner <address> \
//     --assume-balance WETH:<amountIn>n \
//     --args <amountIn> <sqrtPriceLimitX96> <due> <payee> <refundTo>
//   sauce run swap-then-pay.sauce.ts --chain base --wallet <name> --fund WETH:<amountIn>n \
//     --args <amountIn> <sqrtPriceLimitX96> <due> <payee> <refundTo>
//
//   amountIn: Uint256           the WETH to sell, in wei (18 decimals): 500000000000000000 is
//                               0.5 WETH
//   sqrtPriceLimitX96: Uint256  the lowest pool price the swap may reach, as a sqrtPriceX96; WETH
//                               sorts before USDC on Base, so it is a floor. `sauce price --chain
//                               base 2200 USDC per WETH` prints it (Selling WETH). 0 sets no floor
//   due: Uint256                the invoice, in USDC's base units (6 decimals): 1000000000 is
//                               1,000 USDC
//   payee: Address              who the invoice is paid to
//   refundTo: Address           who receives the USDC above `due` and the unsold WETH
//
// On Base, sell 0.5 WETH with a floor of 2,200 USDC per WETH and pay a 1,000 USDC invoice to <payee>:
//
//   sauce run swap-then-pay.sauce.ts --chain base --wallet me --fund WETH:0.5 \
//     --args 500000000000000000 3716130220787573219086287 1000000000 \
//     <payee> <refundTo>
import { uniswapV3 } from "@sauce/router";

function main(
  amountIn: Uint256,
  sqrtPriceLimitX96: Uint256,
  due: Uint256,
  payee: Address,
  refundTo: Address,
): { received: Uint256; paid: Uint256; changeUsdc: Uint256; changeWeth: Uint256 } {
  require(amountIn > 0 && due > 0, "amountIn or due is zero");
  require(payee != 0 && refundTo != 0, "no payee or no refund address");
  require(WETH.balanceOf(pot) >= amountIn, "not enough WETH in the Pot");

  // Swap into the Pot and measure both sides: with a floor, the pool may take less than amountIn.
  const pool = UniswapV3.Factory.getPool(WETH, USDC, 500);
  const wethBefore = WETH.balanceOf(pot);
  const usdcBefore = USDC.balanceOf(pot);
  uniswapV3(pool, WETH, USDC, amountIn, sqrtPriceLimitX96, 0, pot);
  const sold = wethBefore - WETH.balanceOf(pot);
  const received = USDC.balanceOf(pot) - usdcBefore;
  require(received >= due, "the swap returned less USDC than due");

  // Pay the invoice exactly, measured at the payee.
  const payeeBefore = USDC.balanceOf(payee);
  USDC.transfer(payee, due);
  const paid = USDC.balanceOf(payee) - payeeBefore;
  require(paid == due, "the payee received a different amount");

  // Return the change: USDC above the invoice, and WETH the pool did not take.
  const changeUsdc = received - due;
  const changeWeth = amountIn - sold;
  if (changeUsdc > 0) {
    USDC.transfer(refundTo, changeUsdc);
  }
  if (changeWeth > 0) {
    WETH.transfer(refundTo, changeWeth);
  }

  return { received, paid, changeUsdc, changeWeth };
}
```

## How it works

* The pool comes from `UniswapV3.Factory.getPool(WETH, USDC, 500)`, and the swap is `uniswapV3` from `@sauce/router`, which swaps through the Pot's Router into the Pot (`pot` as the recipient).
* The minimum output passed to `uniswapV3` is `0`; the program enforces `received >= due` itself with a reason, so a short swap reverts with "the swap returned less USDC than due" rather than an unlabelled revert.
* `sqrtPriceLimitX96` is a floor because WETH sorts before USDC on Base (WETH is token0), and selling token0 lowers the price. When the floor is reached, the pool takes only part of `amountIn`; the program measures what it sold from the Pot's WETH balance and returns the rest to `refundTo` as `changeWeth`.
* Both sides of the swap are measured as balance changes of the Pot, and the payment is measured at the payee.

## Run the example

**1. Compile**

```bash theme={null}
sauce compile swap-then-pay.sauce.ts --chain base
```

**2. Find the floor**

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

The `sqrtPriceLimitX96` printed under "Selling WETH" is the floor for 2,200 USDC per WETH: `3716130220787573219086287`.

**3. Simulate**

```bash theme={null}
sauce simulate swap-then-pay.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:0.5 \
  --args 500000000000000000 3716130220787573219086287 1000000000 <payee> <refund-to>
```

`--assume-balance WETH:0.5` gives the Pot 0.5 WETH for this simulation only; the invoice is 1,000 USDC.

**4. Send**

```bash theme={null}
sauce run swap-then-pay.sauce.ts --chain base --wallet <name> --fund WETH:0.5 \
  --args 500000000000000000 3716130220787573219086287 1000000000 <payee> <refund-to>
```

`sauce tx` prints the unsigned transaction instead, for your own signer:

```bash theme={null}
sauce tx swap-then-pay.sauce.ts --chain base --owner <your-address> --fund WETH:0.5 \
  --args 500000000000000000 3716130220787573219086287 1000000000 <payee> <refund-to>
```

## Simulation result

Simulated on a local fork of Base at block 53159700 with the current contracts, with the command above, paying `0x7099…79C8` and refunding to `0x3C44…93BC`. `0xd0b5…F224` is the Uniswap V3 0.05% WETH/USDC pool (`sauce pools --chain base WETH USDC` lists it). Nothing was signed or sent.

```text theme={null}
The cook succeeds from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: gas used 270901 for the cook, after the Pot's deploy (1. below), returns 0x000000000000000000000000000000000000000000000000000000004551cea1000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000009b704a10000000000000000000000000000000000000000000000000000000000000000
main returns { received: Uint256, paid: Uint256, changeUsdc: Uint256, changeWeth: Uint256 }: { received: 1162989217, paid: 1000000000, changeUsdc: 162989217, changeWeth: 0 }
...
  2. the cook, from the owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, gas used 270901
Assumed for this simulation only, by state override:
  the Pot holds 0.5 WETH: --assume-balance (its balance word 0x414b65ef17cbb779af5e33937c81e6b35b67a6491f0cc46ef530a2f31a50c704: Solidity mapping at slot 3)
What moved, in order:
...
  [3] 0xd0b5…F224        -> Pot                  1162.989217 USDC
  [4] Pot                -> 0xd0b5…F224          0.5 WETH
  [5] Pot                -> 0x7099…79C8          1000 USDC
  [6] Pot                -> 0x3C44…93BC          162.989217 USDC
Net balance changes:
  0xd0b53D9277642d899DF5C87A3966A349A798F224: -1162.989217 USDC, +0.5 WETH
  0xDcFCc75A3C9a0EC798CAf81B6C84D4c5d5988c49 (the Pot): -0.5 WETH
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8: +1000 USDC
  0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC: +162.989217 USDC
  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (owner): -0.00000374478538635 ETH
```

The pool took all 0.5 WETH for 1,162.989217 USDC without its price reaching the 2,200 floor. The payee received exactly 1,000 USDC and `refundTo` the other 162.989217 USDC; no WETH was left over.

On the same fork, with a floor of 2,327.14 USDC per WETH (`sauce price --chain base 2327.14 USDC per WETH` gives `3822001377876224716099186`, below the pool's price of 2,327.20 before the fee as `sauce pools` read it) and a 100 USDC invoice:

```bash theme={null}
sauce simulate swap-then-pay.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:0.5 \
  --args 500000000000000000 3822001377876224716099186 100000000 <payee> <refund-to>
```

```text theme={null}
main returns { received: Uint256, paid: Uint256, changeUsdc: Uint256, changeWeth: Uint256 }: { received: 600197430, paid: 100000000, changeUsdc: 500197430, changeWeth: 241962506158517737 }
...
  [3] 0xd0b5…F224        -> Pot                  600.19743 USDC
  [4] Pot                -> 0xd0b5…F224          0.258037493841482263 WETH
  [5] Pot                -> 0x7099…79C8          100 USDC
  [6] Pot                -> 0x3C44…93BC          500.19743 USDC
  [7] Pot                -> 0x3C44…93BC          0.241962506158517737 WETH
```

The price reached the floor after 0.258037493841482263 WETH, so the pool stopped there. The program paid the 100 USDC invoice and sent 500.19743 USDC and the 0.241962506158517737 unsold WETH to `refundTo`.

On the same fork, the guard, with a 5,000 USDC invoice:

```bash theme={null}
sauce simulate swap-then-pay.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:0.5 \
  --args 500000000000000000 3716130220787573219086287 5000000000 <payee> <refund-to> \
  --expect-revert "the swap returned less USDC than due"
```

```text theme={null}
As expected, the cook reverts: the program threw "the swap returned less USDC than due"
The cook reverts: the program threw "the swap returned less USDC than due"
```

0.5 WETH returned about 1,163 USDC, below the invoice, so the cook reverted and nothing moved.

## Next steps

* [Quote two pools, swap, supply](https://docs.eco.com/quickstarts/sauce/quote-swap-supply.md): choose between two fee tiers before the swap
* [Pay USDC from the Pot](https://docs.eco.com/quickstarts/sauce/pay.md): the payment alone
* [`sauce price`](https://docs.eco.com/sauce-sdk/cli/price), [`sauce pools`](https://docs.eco.com/sauce-sdk/cli/pools), [`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.