Skip to content

Gasless swaps with Permit2

Let a user swap a token while holding zero native gas token. The user signs one typed-data message; Vector pulls the input token with that signature, executes the swap, and delivers the output — with gas relayed and paid through Photon.

Prerequisites

  • An API key.
  • A chain whose features include gaslessSwaps and permit2 — check Supported networks.
  • One-time Permit2 approval: the user must have approved the canonical Permit2 contract for the input token (approve(permit2, max)), once per token. This is the only transaction the user ever pays for — every swap after it is gasless. New tokens acquired via transfer (not swap) commonly already have this done by the sending platform.

1. Get a gasless quote

acquisitionMode is required: 2 (Permit2) or 3 (Permit2Witness — the signature additionally binds the exact calldata; prefer it when available):

curl "https://vector.example.com/gasless/quote?fromChain=84532&toChain=84532&fromToken=0xUSDC…&toToken=0xWETH…&amount=25000000&sender=0xUser…&acquisitionMode=3" \
  -H "api-key: $EDITH_API_KEY"

The response is an array of gasless quotes. Each contains:

  • quote — the underlying route (output, fees, path), as in Get quote routes;
  • fee — the gasless service fee taken in the input token (feeAmount, feeUsd ≈ $0.01, see Fees);
  • nonce, deadline — Permit2 signature parameters;
  • eip712 — the complete typed-data payload to sign, ready for signTypedData. In mode 3 its message includes the witness object binding callDataHash and target.

2. The user signs

const q = quotes[0];
const signature = await walletClient.signTypedData({
  domain: q.eip712.domain,
  types: q.eip712.types,
  primaryType: q.eip712.primaryType,
  message: q.eip712.message,
});

No gas, no transaction — just a signature over the permit (and, in mode 3, the witness).

3. Submit

curl -X POST https://vector.example.com/gasless/submit-swap \
  -H "api-key: $EDITH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "quoteId": "'"$QUOTE_ID"'", "signature": "'"$SIGNATURE"'" }'

Vector verifies the signature, then executes: the input token is pulled via Permit2, the fee is skimmed, the swap runs, and the output goes to the receiver. The response carries a swapId and a photonTxId — the relayed execution as seen by Photon.

4. Track to executed

curl "https://vector.example.com/gasless/status?id=$SWAP_ID" \
  -H "api-key: $EDITH_API_KEY"

Status runs pending → submitted → executed (or failed). executed includes the executionTxHash. For relayer-level detail (cost, retries), follow the photonTxId on Photon's API.

Failure modes to handle

Symptom Cause Handling
Build/submit rejected with expired quote expiry passed Re-quote; never re-sign an old payload.
failed status after submit On-chain revert (e.g. liquidity moved beyond minAmountOut) Re-quote and offer the user the new rate.
Signature rejected Signed payload didn't match the quote (wrong account, altered fields) Sign the eip712 payload exactly as returned.

Info

Mode 3's witness means a leaked signature is useless for anything except the exact swap the user approved — there is nothing to replay against other calldata or targets.