Skip to main content
The program offers each pool in turn what is left of the budget, in the program’s order and not in order of price, and each pool trades until its own fee-adjusted price reaches priceLimit. The cook reverts when the budget is not in the Pot, fewer than minTokens SPCX_US are bought, or the average price is above maxAvgPrice; the SPCX_US bought and the unspent USDC go to recipient. Target chains: Solana. Use --chain solana. The example uses the SPCX_US Token-2022 mint.

Prerequisites

You need the sauce command (npm install -g @eco-incorp/sauce), a Solana key to send from (a keypair file, or sauce wallet new <name> --solana), and the budget in the Pot’s USDC token account. --fund is EVM only: transfer the USDC to that token account before running the cook, not to the Pot’s address. sauce pot --chain solana --owner <your-pubkey> prints the Pot’s address; use it as the token account’s owner. See Installation.

Program

pot is the Pot the program runs on, available without declaring it; pot.USDC reads its USDC token account (Names).
solana-distributed-swap.sauce.ts

How it works

  • Each <Venue>.swap(pool, tokenIn, tokenOut, amountIn, priceLimit, minOut) is @sauce/router’s swap at that venue. It returns 0 having moved nothing for a key with no pool, a spent budget, an empty or past-limit pool, and a pool whose fee is not one rate; otherwise it trades up to the pool’s own limit, priceLimit moved by the pool’s fee, and returns what arrived in the Pot.
  • kept is the Pot’s USDC beyond budget, so each pool is offered only pot.USDC - kept. The totals are checked once, after every pool: bought >= minTokens, and spent * 10 ** 18 <= maxAvgPrice * bought unless maxAvgPrice is 0.
  • The pools fill in the program’s order, not in order of price: each swap trades up to its pool’s own limit before the next pool is offered what is left. sauce pools listed Raydium’s 0.25% pool at 147.93 USDC a share with its fee, Byreal’s at 147.79 and PancakeSwap’s at 147.77. With priceLimit at 150, Raydium’s pool, first in the program, took the whole 1,000 USDC for 6.75963 SPCX_US, an average of 147.94, and the two cheaper pools were offered nothing (the command’s local LiteSVM copy of Solana at slot 460565379). At 147.90 (147900000000000000000), below Raydium’s price with its fee, Byreal’s and PancakeSwap’s pools filled 446.356017 USDC for 3.019138 SPCX_US, an average of 147.84, and 553.643983 USDC came back unspent (the same local copy at slot 460565433). A priceLimit close to the prices sauce pools lists keeps every pool near them.
  • The keys are written in, and the command reads each key’s nine accounts from its pool when it builds the cook. On the slots simulated, the Raydium pool at fee 0.18% could not be read, and the command refused the cook before simulating (exit 2). The commands below pass that key’s nine accounts as the System Program, the form a key with no pool takes, so its swap returns 0 and the program runs unchanged.
  • Each swap walks only the tick arrays its slot gives, and past them a venue’s program fails the whole cook, so each swap at a price stops a tick inside them. On Raydium CLMM, Byreal and PancakeSwap CLMM, that window ends where the furthest of those arrays begins; where a pool’s limit takes it past there, the command reads that array’s last initialized tick and the swap walks on to a tick inside it. It also holds a pool short of its limit, or skips it, where the cook’s compute units would not cover its walk. With a budget of 100,000 USDC at slot 460512539, the PancakeSwap pool walked on (deepTick 50132 in computeStops). Swaps at a price and compute units explains the stops.
  • sauce compile --json measures the program at 5,329 bytes and the cook at 7,382 bytes against the 1,232-byte limit. sauce run stages the program in a code buffer and passes the cook’s accounts through an address lookup table it creates.

Run the example

1

Read the pools

The listing gives each pool’s key (found by key: Raydium.CLMM.pool(USDC, SPCX_US, 2500)), fee, liquidity and price paying USDC, with the fee and as a priceLimit, and lists the pools a distributed swap skips, with why. Here it listed pool HnBT4gtWyTNKz1VuCrg5DfC57FZSnoSLGrQ2Vjpyfkts, the one the program’s key 1800 finds, among them: its fee is more than one rate (a pool rate, decay, imbalance or dynamic fee).
2

Compile

3

Simulate

--assume-balance USDC:1000 gives the Pot’s USDC account the budget in a local LiteSVM copy of the cook’s accounts, read from the cluster. Without the nine --account flags the command stopped with:
4

Send

maxAvgPrice here is halfway from the simulated average to priceLimit, rounded up; a simulation with it at slot 460261350 succeeded. The nine --account flags are needed only while the command cannot read the 0.18% pool’s accounts: run the Simulate step without them first, and add them if it stops with the message above. sauce run creates the Pot and the missing token accounts the cook names (asking first, or with --yes), stages the program, creates the lookup table, sends the cook and closes the buffer. sauce tx with --owner <your-pubkey> and the Simulate step’s --args and nine --account flags printed a setup step of 14 unsigned transactions for another signer.
recipient is a wallet’s public key. A token account given there is taken as a wallet, and the cook pays an associated token account it holds, which no one can spend from.

Simulation result

Simulated on Solana at slot 460261190 with the Simulate command above, in a local LiteSVM copy. Nothing was signed or sent; values change with the slot. Lines that name the owner are left out.
Raydium’s 0.25% pool took the whole budget: 1,000 USDC for 6.750167 SPCX_US, an average of 148.14 USDC per share (spent × 10^18 ÷ bought is 148144482943903461943), inside the 150 limit. Byreal’s and PancakeSwap’s pools show no balance change: the budget was spent before their turn. With minTokens at 6.8 SPCX_US (6800000) and --expect-revert "bought below minTokens":

Next steps