Skip to content

GuidesPermit2

Swaps

Permit2

Permit2 replaces "approve the Executor for every token" with one approval to Permit2 per token, then an off-chain signature per swap. The canonical Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3) is deployed on Elysium testnet and mainnet.

ERC20 inputs only. Native HYPE needs no approval.

Why use it

Classic approvePermit2
First swap of a tokenapprove tx + swap txapprove-Permit2 tx (once) + signature + swap tx
Next swapsswap tx (if the allowance remains)signature + swap tx
Allowance left behindWhatever you approved, on the ExecutorInfinite on Permit2, but each signature is single-use, bound to an amount, a deadline, the Executor and your address

The two-call flow

Call 1: get the data to sign

curl "$API/quote?in=$USDC&out=HYPE&amount=25000000&recipient=$ME&permit2=true"
  • If approval is not null: it approves Permit2 (unlimited, once per token). Send it, then repeat call 1.
  • permit2.typedData is an EIP-712 PermitTransferFrom:
"permit2": { "typedData": {
  "domain": { "name": "Permit2", "chainId": 99801, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" },
  "types": {
    "PermitTransferFrom": [ { "name": "permitted", "type": "TokenPermissions" }, { "name": "spender", "type": "address" },
                            { "name": "nonce", "type": "uint256" }, { "name": "deadline", "type": "uint256" } ],
    "TokenPermissions": [ { "name": "token", "type": "address" }, { "name": "amount", "type": "uint256" } ]
  },
  "primaryType": "PermitTransferFrom",
  "message": { "permitted": { "token": "0x7D29…", "amount": "25000000" }, "spender": "0xExecutor…", "nonce": "8172…", "deadline": "1762450000" }
} }
  • tx.data is 0x and simulation.ok is false with "sign permit2.typedData, then re-quote…". That is expected.

Sign it

const signature = await wallet.signTypedData({ account, ...q.permit2.typedData });

Call 2: get the transaction

Repeat the quote with the signature, and the same nonce and deadline as in the message you signed:

curl "$API/quote?in=$USDC&out=HYPE&amount=25000000&recipient=$ME&permit2=true\
&signature=0x…&permit2Nonce=8172…&permit2Deadline=1762450000"

tx now calls Executor.swapWithPermit2(...) and is simulated as usual. Send it.

Rules enforced on-chain

  • The permit's spender is the Executor, and the Executor passes msg.sender as the owner to Permit2. A signature is useless to anyone except the address that signed it, even if it leaks.
  • Nonces are unordered and single-use (the API picks a random 192-bit one). A signature can't be replayed.
  • amount must equal the swap's amountIn. permit2Deadline bounds the signature; the swap's own 5-minute deadline still applies.
  • If the route changes between call 1 and call 2, the signature stays valid: it only covers the token, the amount and the spender, not the route.

Next: Fees

lane.markets · Make the most of every move.