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

# Dollar-cost averaging: one step of a buy plan

> Run one step of a WETH purchase plan on Base or Ethereum, buying within a price limit or skipping the purchase.

Each run spends up to `tranche` of the Pot's USDC on WETH in Uniswap V3's 0.05% WETH/USDC pool, as long as one WETH, fee included, costs at most `priceLimit`. When the price is above the limit or the Pot holds less than a tranche, the run returns without trading. The cook reverts when the pool does not exist or the average price paid exceeds `priceLimit`.

**Target chains:** Base and Ethereum. The same source compiles for both; the recorded simulation uses a Base fork.

This program evaluates conditions only when called. To run it repeatedly, use an external scheduler or caller that submits an authorized transaction.

## 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 native coin for gas and the Kitchen fee. Fund the Pot once it is deployed, or use [`--fund`](https://docs.eco.com/sauce-sdk/cli/funding); it needs at least `tranche` USDC when you run the program.

## Program

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

```ts dca.sauce.ts theme={null}
// One tick of a dollar-cost-averaging plan, meant to run on a schedule. It spends up to `tranche`
// of the Pot's USDC on WETH through Uniswap V3's 0.05% WETH/USDC pool, and only while one WETH
// costs at most `priceLimit` USDC, pool fee included. The swap stops at the pool price that
// corresponds to `priceLimit`, so a thin pool can fill part of the tranche; the rest stays in the
// Pot. Above the limit, or with less than `tranche` USDC in the Pot, the tick buys nothing and
// returns normally. It reverts when the pool does not exist or the average price paid exceeds
// `priceLimit`. The WETH stays in the Pot.
//
//   sauce simulate dca.sauce.ts --chain <chain> --owner <address> \
//     --assume-balance USDC:<tranche>n --args <tranche> <priceLimit>
//   sauce run dca.sauce.ts --chain <chain> --wallet <name> --args <tranche> <priceLimit>
//
//   tranche: Uint256     the USDC to spend in one tick, in USDC base units (6 decimals):
//                        250000000 is 250 USDC
//   priceLimit: Uint256  the most one WETH may cost, fee included, in USDC base units:
//                        2800000000 is 2,800 USDC
//
// On Base, spend 250 USDC while WETH costs at most 2,800 USDC:
//
//   sauce run dca.sauce.ts --chain base --wallet me --args 250000000 2800000000
import { uniswapV3 } from "@sauce/router";

function main(
  tranche: Uint256,
  priceLimit: Uint256,
): { price: Uint256; spent: Uint256; bought: Uint256 } {
  require(tranche > 0 && priceLimit > 0, "set tranche and priceLimit");
  const pool = UniswapV3.Factory.getPool(WETH, USDC, 500);
  require(pool != 0, "no WETH/USDC pool at 0.05%");
  const fee = UniswapV3.Pool(pool).fee();

  // The pool's price in USDC base units per WETH, from its sqrt price (token1 per token0, x 2^96).
  const sqrtPriceX96 = UniswapV3.Pool(pool).slot0().sqrtPriceX96;
  const ratio = Math.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 96);
  const price =
    WETH < USDC ? Math.mulDiv(ratio, 10 ** 18, 1 << 96) : Math.mulDiv(10 ** 18, 1 << 96, ratio);
  const usdcBefore = pot.USDC;
  const wethBefore = pot.WETH;

  // Skip the tick: the price with the fee on top is above the limit, or the Pot holds too little.
  if (Math.mulDiv(price, 1000000, 1000000 - fee) > priceLimit || usdcBefore < tranche) {
    return { price, spent: 0, bought: 0 };
  }

  // The pool price at which WETH, fee included, costs exactly priceLimit, as a sqrt price. The
  // swap stops there. Paying token1 pushes the price up, paying token0 pushes it down.
  const atLimit = Math.mulDiv(priceLimit, 1000000 - fee, 1000000);
  const limit =
    WETH < USDC
      ? Math.sqrt(Math.mulDiv(atLimit, 1 << 192, 10 ** 18))
      : Math.sqrt(Math.mulDiv(1 << 192, 10 ** 18, atLimit));

  // The limit decides how much is bought, so no minimum output is known before the swap. The
  // average price is checked after it, from what the Pot's balances show moved.
  uniswapV3(pool, USDC, WETH, tranche, limit, 0, pot);
  const spent = usdcBefore - pot.USDC;
  const bought = pot.WETH - wethBefore;
  require(Math.mulDiv(bought, priceLimit, 10 ** 18) >= spent, "paid more than priceLimit");
  return { price, spent, bought };
}
```

## How it works

* The program reads the pool price from `slot0().sqrtPriceX96` and converts it to USDC base units per WETH. The `WETH < USDC` comparison picks the formula, because the pool orders its tokens by address and that order differs between chains.
* A skipped tick returns `spent: 0` instead of reverting, so a scheduler that runs it every hour sees a successful cook. A skipped tick still costs gas and the Kitchen's fee, which the cook pays in ETH.
* The swap's price limit is the sqrt price at which WETH, after the 0.05% fee, costs `priceLimit`. The pool stops filling there, so a tranche can be filled in part; the unspent USDC stays in the Pot for the next tick.
* `spent` and `bought` are the changes in the Pot's USDC and WETH balances. The final `require` checks the average price from those, independent of the router's return value.

## Run the example

**1. Compile**

```bash theme={null}
sauce compile dca.sauce.ts --chain base
sauce compile dca.sauce.ts --chain ethereum
```

**2. Simulate**

```bash theme={null}
sauce simulate dca.sauce.ts --chain base --owner <your-address> \
  --assume-balance USDC:250 --args 250000000 2500000000
```

`--assume-balance USDC:250` gives the Pot 250 USDC for this simulation only. The result lists the pool price the tick read and what the Pot spent and received.

**3. Send**

```bash theme={null}
sauce run dca.sauce.ts --chain base --wallet <name> --args 250000000 2500000000
```

Add `--fund USDC:250` to pull the tranche from your wallet into the Pot in the same cook. `sauce tx dca.sauce.ts --chain base --owner <your-address> --args 250000000 2500000000` prints the unsigned transactions instead, for your own signer or scheduler.

## Simulation result

Simulated on a local fork of Base at block 53159700 with the current contracts, a tranche of 250 USDC with `priceLimit` 2,500 USDC. Nothing was signed or sent.

```text theme={null}
main returns { price: Uint256, spent: Uint256, bought: Uint256 }: { price: 2327204378, spent: 250000000, bought: 107370699143671980 }
Left in the Pot, not in the owner's wallet:
  0.10737069914367198 WETH
...
What moved, in order:
...
  [3] 0xd0b5…F224        -> Pot                  0.10737069914367198 WETH
  [4] Pot                -> 0xd0b5…F224          250 USDC
```

The pool priced WETH at 2,327.204378 USDC, under the limit, so the tick bought. It spent the whole 250 USDC for 0.10737069914367198 WETH, an average of about 2,328.38 USDC per WETH with the 0.05% fee included. The WETH stays in the Pot.

On the same fork, with `priceLimit` set to 1,000 USDC, under the market, the tick skips:

```bash theme={null}
sauce simulate dca.sauce.ts --chain base --owner <your-address> \
  --assume-balance USDC:250 --args 250000000 1000000000
```

```text theme={null}
main returns { price: Uint256, spent: Uint256, bought: Uint256 }: { price: 2327204378, spent: 0, bought: 0 }
```

The cook succeeds and no token moves; the only transfer is the Kitchen's fee in ETH. The 250 USDC stays in the Pot.

## Next steps

* [Stop-loss and take-profit](https://docs.eco.com/quickstarts/sauce/stop-loss.md): sell the WETH the plan accumulates when an oracle price crosses a threshold.
* [Buy the dip](https://docs.eco.com/quickstarts/sauce/buy-the-dip.md): buy a tranche only after the oracle price falls a set percentage.
* [Rebalance](https://docs.eco.com/quickstarts/sauce/rebalance.md): hold WETH and USDC at a target share of value.
* [Sweep the Pot](https://docs.eco.com/quickstarts/sauce/sweep-pot.md): move what the Pot holds to a wallet.
* [`sauce simulate`](https://docs.eco.com/sauce-sdk/cli/simulate), [`sauce tx`](https://docs.eco.com/sauce-sdk/cli/tx), [The Pot](https://docs.eco.com/sauce/pot.md).


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