Skip to main content
The program reads Chainlink’s ETH/USD price and the Uniswap V3 WETH/USDC 0.05% pool’s price, then sells up to amount WETH in that pool with a price limit derived from the oracle. The USDC and any WETH the limit leaves unsold go to recipient. The cook reverts when the oracle’s price is older than maxAge, the pool’s price is more than maxDeviationBps from it, the pool is already past the limit, or the recipient receives less than minOut USDC. Target chains: Ethereum. The recorded simulation uses an Ethereum fork.

Prerequisites

Install the Sauce CLI 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.

Program

pot is the Pot the program runs on, available without declaring it (Names).
oracle-guarded-swap.sauce.ts

How it works

  • Two checks run before any swap: the feed’s updatedAt must be within maxAge seconds of the block’s timestamp, and the pool’s price within maxDeviationBps of the oracle’s. The second is written gap * 10000 <= oracle * maxDeviationBps, so no division rounds the bound. The feed’s answer is an Int256; the program requires it above 0 and converts it with Uint256(round.answer), a checked conversion, so the unsigned arithmetic after it never sees a negative price.
  • The price limit comes from the oracle, not the pool. floor is the oracle’s price less maxDeviationBps; as a sqrt price it is moved by √(1 − fee), with the fee read from the pool in the cook, so the last unit the pool fills fetches floor after the 0.05% fee. USDC is token0 on Ethereum, so selling WETH raises the pool’s sqrt price and the limit is a ceiling.
  • A limit on the wrong side of the pool’s current price makes a Uniswap V3 pool revert with SPL; the program checks that first and reverts with the pool is past the limit.
  • received is the recipient’s USDC balance change and sold the Pot’s WETH balance change. The average is held to the floor (sold below the floor), and only amount - sold WETH goes back to the recipient; other WETH in the Pot stays.

Run the example

1

Compile

2

Simulate

--assume-balance WETH:1 gives the Pot 1 WETH for this simulation only. The result lists the oracle’s price, the pool’s price, the WETH sold and the USDC received.
3

Send

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

Simulation result

Simulated on a local fork of Ethereum at block 26278321 with the current contracts. Nothing was signed or sent; values change with the block.
The oracle’s price was 2,327.825213 USD per ETH and the pool’s 2,332.568846 USDC per WETH, about 0.2% apart at 1 USDC = 1 USD, inside the 0.5% allowed. The program sold the whole 1 WETH for 2,331.349886 USDC, paid to the recipient. A larger order stops at the limit. On the same fork, selling 300 WETH with maxDeviationBps at 40:
The program sold 114.651845534299993538 WETH for 266,566.645124 USDC and stopped at the price limit, then sent the other 185.348154465700006462 WETH to the recipient. On the same fork, with maxDeviationBps at 0 the pool’s price must equal the oracle’s, and the cook reverts at that check:

Next steps