Swaps
Build and execute
Everything about turning a quote into a mined swap.
The flow
- Quote:
GET /v1/quote?in&out&amount&recipient[&sender][&slippageBps]. - Approve if
approvalis not null (ERC20 input), then re-quote. Or use Permit2. - Check
simulation.okandwarnings. - Sign and send
txunchanged. - Confirm: the receipt's
status, and the Executor'sSwappedevent for the exactamountOutandfee.
Parameters that shape the transaction
| Parameter | Default | Effect |
|---|---|---|
amount | required | Exact input. The router finds the best output for it (exact-in only; there is no exact-out mode) |
recipient | required | Receives the output. Can differ from the signer |
sender | recipient | Who signs and pays. Used for the balance, allowance and gas simulation |
slippageBps | 50 (0.5%) | minOut = amountOut × (10,000 − slippageBps) / 10,000. Range 0–5000 |
Set by the API, inside tx.data:
| Value | Rule |
|---|---|
minOut | From slippageBps, applied to the output after fee and measured taxes |
deadline | Quote time + 300 s. After it, the swap reverts with Expired() |
wrappedNative | The WHYPE used to wrap/unwrap native HYPE on this route |
splits | The legs and hops of the route (encoding) |
Slippage, minOut and deadline
| Situation | What happens |
|---|---|
| Price moves in your favor | You receive more than amountOut. All of it goes to recipient (minus the fee on the larger amount) |
| Price moves against you, within slippage | You receive between minOut and amountOut |
| Price moves beyond slippage | Revert InsufficientOutput(amountOut, minOut). Nothing moves except gas |
| Sent after the deadline | Revert Expired() |
| A pool can't absorb the whole input (price limit reached) | Revert PartialFill() |
minOut is checked on what the recipient actually receives (its balance difference), so a token that taxes transfers to the recipient can't sneak under the floor.
Choosing slippage: 0.3–0.5% for liquid pairs; 1–3% for small caps; more for taxed tokens only if taxBps doesn't already cover the tax (it's already deducted from amountOut).
Auto slippage
slippageBps=auto applies the recommendation the quote always returns in slippage.recommendedBps:
| Part | bps |
|---|---|
| Floor (normal drift between quote and inclusion) | 30 |
| Price impact of the trade | + half of it |
| Each swap beyond the first | + 20 |
| A taxed token in the trade | + 100 |
| A hooked v4 pool on the route (dynamic fees) | + 50 |
| Range | 30 – 1500 |
| Launchpad bonding curve | 300 |
What the quote tells the user
| UI element | Field |
|---|---|
| "+$4.12 vs one pool" | savings.usd (and savings.vsBestSinglePool in tokens): our output minus the exact quote of the best route through a single pool |
| "37 pools scanned" | scanned.pools |
| Output in dollars | amountOutUsd |
Approvals
The spender is always the Executor (or Permit2 on the Permit2 path; the OrderBook for orders; the launchpad router for venue: "launchpad"). approval is pre-built with the right spender and the exact amount:
"approval": { "to": "0xTokenIn…", "data": "0x095ea7b3…" }Send it with value 0, wait for it to be mined, then quote again. The API approves the exact amount, not unlimited. If you want an unlimited approval, build it yourself (approve(executor, 2^256-1)).
Native HYPE
| Direction | How |
|---|---|
| HYPE in | in=HYPE. tx.value = amount. The Executor wraps it into wrappedNative |
| HYPE out | out=HYPE. The Executor unwraps at the end and sends HYPE to recipient |
| HYPE → WHYPE, or WHYPE → WHYPE | Routed as a wrap/rewrap hop at 1:1 |
wrappedNative in the response says which WHYPE the route used. Elysium has several, and the router tries all the allow-listed ones.
Splits and multi-hop routes
splits is a list of legs. Each leg takes part of amountIn and follows its own route of 1–3 swaps (plus an optional rewrap):
"splits": [
{ "amountIn": "600000000000000000", "route": [ { "protocol": "v3", … } ] },
{ "amountIn": "400000000000000000", "route": [ { "protocol": "v2", … }, { "protocol": "v3", … } ] }
]- Legs share no pool (so one leg's price impact doesn't distort another's quote) and end in the same
tokenOut. - The fee and
minOutapply once, to the total output. protocolisv2,v3,v4,wrap(WHYPE↔WHYPE rewrap) orlaunchpad.
Simulation
The API never returns an unchecked transaction:
simulation | Meaning | What to do |
|---|---|---|
{ ok: true, gas } | The exact tx succeeds from sender at the current block | Send. Use gas (+ a margin) as the gas limit if you like |
{ ok: false, error: "insufficient allowance: …" } | approval must be sent first | Send approval, re-quote |
{ ok: false, error: "insufficient balance" } | sender doesn't hold amount | — |
{ ok: false, error: "<revert reason>" } | The swap would revert (blocked token, hook, …) | Don't send. tx.warning is also set |
sender must be the address that will really sign; with the default (recipient), make sure that is the case.
Launchpad tokens
Before graduating, some tokens can only be bought or sold through their launchpad's bonding curve (their transfers block contracts). When that curve pays more than the aggregator, the quote comes back with:
venue: "launchpad",feeBps: 0(no router fee; the curve takes its own 1% tax),tx.to= the launchpad router, andapprovaltoward it,- a warning
launchpad token (pre-graduation)….
Everything else (simulation, minOut, deadline) works the same.
Confirming the result
The Executor emits, on every swap:
event Swapped(address indexed sender, address indexed recipient, address tokenIn, address tokenOut,
uint256 amountIn, uint256 amountOut, uint256 fee);amountOut is what the recipient received; fee is the router fee taken. GET /v1/history?owner= lists them.
Next: Permit2