sauce command’s output and include them in a custom route.
Prerequisites
- Complete the Sauce quickstart and read Destination calls.
- Install the Sauce command and choose a program that compiles for your destination chain.
- Identify the destination Portal and Executor, and verify the Sauce release deployments on that chain.
- Set up Routes API access if you use custom quotes. Direct Portal integration has its own publishing and funding requirements.
What runs atomically
The destination fulfillment is one transaction:- The Portal checks the route against the intent hash, records the claimant, and pulls
route.tokensfrom the solver into its Executor. - The Executor runs
route.callsin order. Each call’s target sees the Executor asmsg.sender. - One of those calls cooks a program on a Pot. The program’s reads, checks and protocol calls run as the Pot, inside that call.
require in the program, a protocol’s own revert, or a cook sent without the Kitchen’s fee. Source-chain funding and the proof that releases the solver’s reward are separate transactions. See Destination calls.
The route’s calls, and with them the program and its main arguments, are fixed when the intent is published. The program reads state when the solver fulfills, so a minimum output or a balance check holds at execution time.
The destination calls
<executor> is the account every call comes from: read executor() on the destination Portal. Set route.tokens to cover call 2, and route.nativeAmount to the cook’s value: the Portal forwards nativeAmount to the Executor, which sends each call’s value. A value above the Kitchen’s fee stays in the Pot.
Build and verify the route
Save the approval and swap program below asapprove-swap.sauce.ts. Replace the command placeholders with your destination Executor, minimum WETH output and recipient. The examples use Base (chain ID 8453); consult supported chains and tokens for Routes availability and the Sauce chain reference for program names.
1
Simulate the cook on the Executor's Pot
--assume-balance gives the Pot the USDC that call 2 will move into it, for this simulation only.2
Print calls 1 and 3
3
Request and verify the quote
The Routes API takes destination calls in a
custom quote, as destination.calls (Request a quote). Verify the quote signature and execution data, check that intent.route.calls contains your three calls in order, and check that intent.route.tokens delivers at least what call 2 moves. Check the native amount, recipient and output constraints before funding.4
Simulate the fulfillment
Call
fulfill on the destination Portal with eth_call, from an account that holds and has approved the route’s tokens, with route.nativeAmount as its value. Estimate gas for the complete fulfillment, including Pot deployment when needed. A successful standalone cook simulation does not validate the other destination calls.5
Fund and track the intent
After verification and simulation, follow the Routes API integration guide to fund the quote and track its status. If you integrate directly with contracts, use the Portal publishing and funding flow.
Portal.fulfill whose route made these three calls with approve-swap.sauce.ts and 100 USDC deployed the Executor’s Pot, swapped, and paid 0.041672 WETH to the recipient. The Pot and the Executor kept no USDC. A route whose program set minWeth above what the pool paid reverted the whole fulfillment, and so did a route whose cook carried no fee.
The Pot the Executor owns
A Pot acceptscook only from its owner, and every call of a route comes from the destination Portal’s Executor, so the Pot a route cooks on belongs to the Executor. That Executor runs the calls of every route fulfilled on its chain. Any route can cook any program on a Pot the Executor owns, whatever its salt.
On a local fork of Base at block 52985412 with the current contracts, 5 USDC left in the Executor’s Pot were taken by another account’s route, whose program transferred the Pot’s USDC to that account. The Executor applies the same rule to its own balance: it does not sweep what a route leaves behind. See Security considerations.
A signed cook on your own Pot
Private Pots also takecookSigned: the Pot’s owner signs one cook, and another account submits it (Signed cooks). In a route, call 2 moves the tokens into your own Pot, and call 3 is cookSigned on that Pot with submitter set to the destination Executor. The program then runs with your Pot’s authority, and what it leaves in your Pot stays yours.
- A submission of that signature by any account other than the Executor reverts with
WrongSubmitter(). - Your Pot pays the Kitchen’s fee, from its own balance, never more than the signed
maxExecutionFee. The signature also fixes the call’svalue(sauce sign --value, default 0): call 3 sends exactly that, androute.nativeAmountcovers it. With a signed value of 0 the fee comes from native coin your Pot already holds; sign a value of at least the fee for the route to pay it. A fee above the Pot’s balance, the call’s value included, or abovemaxExecutionFeefails the whole fulfillment withFeeNotPaid. - The signature is public once the intent is published, and the Executor submits for every route, so another route can submit it first. Its nonce lets it run once. Write the program to be correct whenever it runs, for example by checking the balances it spends.
v12PotAbifrom@eco-incorp/sauce/evm/engineincludescookSigned. Build and sign it withsauce sign --submitter <address>, or in TypeScript withsignedCookTypedDataandencodeCookSignedfrom@eco-incorp/sauce/evm/engine;checkSignedCookPotrefuses an old or Shared Pot before you sign.
supply.sauce.ts supplied the 100 USDC to Aave V3. A second route submitting the same signature reverted with NonceUsed().
Three programs to start from
Each program compiles for Base and can be the cook in call 3. Simulate it from your own Pot first;sauce simulate needs no key.
A one-line approval
approve.sauce.ts
USDC and UniswapV3.SwapRouter02 are names: compiling for Base resolves them to their addresses there. amount is in USDC’s smallest unit (6 decimals). On the Executor’s Pot, this approval stays in place after the cook, so a route’s program spends what it approves, as the next one does.
An approval and a swap
approve-swap.sauce.ts
amountIn USDC on Uniswap V3’s 0.05% USDC/WETH pool and sends the WETH to to. The swap reverts, and the fulfillment with it, when the pool pays less than minWeth wei. With no price limit, minWeth is the swap’s only bound.
A supply with a balance check
supply.sauce.ts
amount USDC to Aave V3, credited to beneficiary, and reverts unless the Pot’s USDC fell by exactly that amount.
Troubleshooting
Next steps
- Pot: its owner, who can cook on it, and signed cooks.
- cook(): what the cook call carries and what it pays.
- sauce tx and sauce pot: every option of the two commands that print the calls.
- Sauce by example: longer programs, from a payment to a swap and supply.
