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

# Rebalance WETH and USDC to a target share

> A Base or Ethereum cook that prices the Pot's WETH and USDC on Uniswap V3 and trades the excess side when WETH's share of value leaves a band around a target.

This program checks the Pot's allocation between WETH and USDC using the price from a Uniswap V3 pool. If WETH's share of the combined value is outside the band around `targetBps`, the program trades toward the target. Inside the band, it returns without trading.

The trade applies a price limit based on `maxSlippageBps`. The cook reverts if the pool is unavailable, the Pot holds neither token, or the average trade price exceeds the configured limit including the pool fee.

**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); the program trades the WETH and USDC the Pot holds.

## 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 rebalance.sauce.ts theme={null}
// Rebalances the Pot's WETH and USDC toward `targetBps` of their combined value in WETH, priced by
// Uniswap V3's 0.05% WETH/USDC pool. Once WETH's share is more than `bandBps` from the target, it
// sells the excess side on that pool, with a price limit `maxSlippageBps` away from the pool's
// price: a pool that reaches the limit fills part of the trade and the rest waits for the next
// run. Inside the band it trades nothing and returns normally. The cook reverts when the pool does
// not exist, the Pot holds neither token, or the trade's average price is past the limit plus the
// pool fee. Both tokens stay in the Pot.
//
//   sauce simulate rebalance.sauce.ts --chain <chain> --owner <address> \
//     --assume-balance WETH:<whole-tokens> --assume-balance USDC:<whole-tokens> \
//     --args <targetBps> <bandBps> <maxSlippageBps>
//   sauce run rebalance.sauce.ts --chain <chain> --wallet <name> \
//     --args <targetBps> <bandBps> <maxSlippageBps>
//
//   targetBps: Uint256       WETH's share of the two by value, in hundredths of a percent:
//                            5000 is half
//   bandBps: Uint256         how far the share may drift before a trade, same units: 500 trades
//                            below 45% or above 55% at a 5000 target
//   maxSlippageBps: Uint256  how far the trade may move the pool's price, in hundredths of a
//                            percent: 30 is 0.3%
//
// On Base, half and half, trading once the share leaves 45-55%, moving the price at most 0.3%:
//
//   sauce run rebalance.sauce.ts --chain base --wallet me --args 5000 500 30
import { uniswapV3 } from "@sauce/router";

// USDC base units per WETH at a pool sqrt price (token1 per token0, x 2^96).
function priceAt(sqrtPriceX96: Uint256): Uint256 {
  const ratio = Math.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 96);
  return WETH < USDC
    ? Math.mulDiv(ratio, 10 ** 18, 1 << 96)
    : Math.mulDiv(10 ** 18, 1 << 96, ratio);
}

// The inverse: the pool sqrt price at `price` USDC base units per WETH.
function sqrtPriceAt(price: Uint256): Uint256 {
  return WETH < USDC
    ? Math.sqrt(Math.mulDiv(price, 1 << 192, 10 ** 18))
    : Math.sqrt(Math.mulDiv(1 << 192, 10 ** 18, price));
}

// WETH's share of the two balances' value at `price`, in hundredths of a percent.
function wethShare(weth: Uint256, usdc: Uint256, price: Uint256): Uint256 {
  const wethValue = Math.mulDiv(weth, price, 10 ** 18);
  return Math.mulDiv(wethValue, 10000, wethValue + usdc);
}

function main(
  targetBps: Uint256,
  bandBps: Uint256,
  maxSlippageBps: Uint256,
): { before: Uint256; after: Uint256; soldWeth: bool; amountIn: Uint256; amountOut: Uint256 } {
  require(targetBps <= 10000 && maxSlippageBps < 10000, "a share above 100%");
  const pool = UniswapV3.Factory.getPool(WETH, USDC, 500);
  require(pool != 0, "no WETH/USDC pool at 0.05%");
  const price = priceAt(UniswapV3.Pool(pool).slot0().sqrtPriceX96);
  const weth = pot.WETH;
  const usdc = pot.USDC;
  require(weth > 0 || usdc > 0, "the Pot holds neither");

  const before = wethShare(weth, usdc, price);
  const soldWeth = before > targetBps;
  if ((soldWeth ? before - targetBps : targetBps - before) <= bandBps) {
    return { before, after: before, soldWeth, amountIn: 0, amountOut: 0 };
  }
  // The value to move, in USDC, to bring WETH to its target share.
  const wethValue = Math.mulDiv(weth, price, 10 ** 18);
  const wanted = Math.mulDiv(wethValue + usdc, targetBps, 10000);
  const fee = UniswapV3.Pool(pool).fee();
  let amountIn: Uint256 = 0;
  let amountOut: Uint256 = 0;
  if (soldWeth) {
    // Sell WETH no lower than `floor`: every unit fetches at least that, less the pool fee.
    const floor = Math.mulDiv(price, 10000 - maxSlippageBps, 10000);
    const sell = Math.mulDiv(wethValue - wanted, 10 ** 18, price);
    uniswapV3(pool, WETH, USDC, sell, sqrtPriceAt(floor), 0, pot);
    amountIn = weth - pot.WETH;
    amountOut = pot.USDC - usdc;
    const least = Math.mulDiv(Math.mulDiv(amountIn, floor, 10 ** 18), 1000000 - fee, 1000000);
    require(amountOut >= least, "sold below the floor");
  } else {
    // Buy WETH no higher than `ceiling`: no unit costs more than that, plus the pool fee.
    const ceiling = Math.mulDiv(price, 10000 + maxSlippageBps, 10000);
    uniswapV3(pool, USDC, WETH, wanted - wethValue, sqrtPriceAt(ceiling), 0, pot);
    amountIn = usdc - pot.USDC;
    amountOut = pot.WETH - weth;
    const most = Math.mulDiv(Math.mulDiv(amountOut, ceiling, 10 ** 18), 1000000, 1000000 - fee);
    require(amountIn <= most + 1, "bought above the ceiling");
  }
  const after = wethShare(pot.WETH, pot.USDC, priceAt(UniswapV3.Pool(pool).slot0().sqrtPriceX96));
  return { before, after, soldWeth, amountIn, amountOut };
}
```

## How it works

* `priceAt` and `wethShare` run twice: before the trade, to decide whether to trade, and after it, to report the new share from the pool's new price. `sqrtPriceAt` runs once, in the branch taken, to turn the floor or the ceiling into the swap's price limit.
* The trade size is computed from the price read before the swap. It ignores the pool fee and the price impact of the swap itself, so it aims at `targetBps` without guaranteeing it, and the price limit can stop the swap early. In either case the next run starts from the new balances.
* `amountIn` and `amountOut` are the changes in the Pot's balances. The `require` in each branch compares them with the limit price, with the pool fee applied, so a fill worse than the limit plus the fee reverts the cook.
* Both tokens stay in the Pot: the swap's recipient is `pot`.

## Run the example

**1. Compile**

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

**2. Simulate**

```bash theme={null}
sauce simulate rebalance.sauce.ts --chain base --owner <your-address> \
  --assume-balance WETH:1 --assume-balance USDC:500 --args 5000 500 30
```

The two `--assume-balance` flags give the Pot 1 WETH and 500 USDC for this simulation only, about 82% WETH by value, outside the 45% to 55% band.

**3. Send**

```bash theme={null}
sauce run rebalance.sauce.ts --chain base --wallet <name> --args 5000 500 30
```

`sauce tx rebalance.sauce.ts --chain base --owner <your-address> --args 5000 500 30` 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, using the Simulate command above. Nothing was signed or sent.

```text theme={null}
main returns { before: Uint256, after: Uint256, soldWeth: bool, amountIn: Uint256, amountOut: Uint256 }: { before: 8231, after: 5000, soldWeth: true, amountIn: 392574970052759156, amountOut: 913126172 }
Left in the Pot, not in the owner's wallet:
  1413.126172 USDC (it held 500 USDC before)
  0.607425029947240844 WETH (it held 1 WETH before)
...
What moved, in order:
...
  [3] 0xd0b5…F224        -> Pot                  913.126172 USDC
  [4] Pot                -> 0xd0b5…F224          0.392574970052759156 WETH
```

WETH was 82.31% of the Pot's value (`before: 8231`), outside the band, so the program sold 0.392574970052759156 WETH for 913.126172 USDC. `after` is 5000, but `wethShare` rounds down by integer division, so that figure alone does not show whether the 0.3% price limit stopped the swap before the target was reached.

## Next steps

* [Dollar-cost averaging](https://docs.eco.com/quickstarts/sauce/dca.md): buy WETH one tranche at a time under a price limit.
* [Holdings per chain](https://docs.eco.com/quickstarts/sauce/holdings-per-chain.md): read what the Pot holds before deciding to trade.
* [Distributed swap](https://docs.eco.com/quickstarts/sauce/distributed-swap.md): split one order across several pools.
* [`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.