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
featuresincludegaslessSwapsandpermit2— 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 forsignTypedData. In mode 3 its message includes thewitnessobject bindingcallDataHashandtarget.
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¶
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.