Skip to main content
Gas fees add layers of complexity to swapping. Holding the native token, on the right network, at the right time to pay for gas adds friction for users. A gasless swap means that the user only pays with the input token, and this covers all fees. This means someone else is executing the transaction and paying the gas.

What solution is right for me?

There are many different solutions when it comes to implementing a gasless swap experience. We’ve put together a decision tree below to help you arrive at the solution that best fits your application.
Same-chain gasless swaps are only supported if you sponsor the fees. Same-chain swaps route directly through DEXes, due to this the solver never has an opportunity to deduct the gas fees before the user gets the destination token.

Permit-based Gasless

Use this approach when your app does not control the user’s wallet (e.g. MetaMask, Rainbow, or any external EOA). The user signs an offchain EIP-3009 or Permit2 message and submits it through the Relay platform. Solvers execute the authorized flow and pay transaction gas.
USDC is fully gasless. Other ERC-20 tokens require a one-time approval transaction. Same-chain ERC-20 → ERC-20 swaps do not support the permit flow: passing usePermit: true is ignored and the quote returns approve and swap transaction steps instead of a permit signature step. Same-chain ERC-20 → native swaps do return the authorize2 permit step.

How it works

1

Get a quote

Call /quote/v2 with usePermit: true
2

Sign the permit

Prompt the user to sign an EIP-712 typed-data permit message (off-chain, no gas)
3

Submit to Relay

Submit the signed permit to Relay
4

Fulfillment

The signed permit authorizes the token transfer. A solver completes the destination transaction using its own capital.

ERC-3009 permits with MulticallAuthorization

From October 26, 2026, Relay routes permits through the v3.2 ApprovalProxy (see Migrating to the new router). On that proxy, an ERC-3009 permit (the USDC style TransferWithAuthorization) needs a second signature: a MulticallAuthorization that ties the permit to the exact calls Relay runs with the user’s tokens. Opt in by passing useMulticallAuthorization: true in the quote request. The permit signature step (authorize1, or authorize2 for a same-chain swap) then holds two items. Sign and post them in this order:
  1. MulticallAuthorization: EIP-712 typed data with the domain name RelayApprovalProxy, version 3.1, the chain id, and the v3.2 ApprovalProxy as the verifying contract. Post it to /execute/permits with kind multicall-authorization. This item has no check, since nothing executes yet.
  2. ERC-3009 authorization: the usual TransferWithAuthorization, posted with kind eip3009. Once both signatures are posted, Relay submits the permit. For a same-chain swap (authorize2), the response returns the swap transaction for the user to send instead.
Posting the ERC-3009 signature first returns a 400. Sign both items as the quote returns them: the ERC-3009 nonce is derived from the MulticallAuthorization, so changing either one invalidates the pair.
Without the opt in, ERC-3009 permits keep using the v3 ApprovalProxy with a single signature during the transition. You can pass the flag before the switch: until then the step holds only the ERC-3009 item. Permit2 permits need no change. On the v3.2 ApprovalProxy the RelayerWitness in the quote’s typed data includes msgValue; if you build Permit2 messages yourself, take the witness from the quote.

Fees

You can choose to subsidize all destination fees by passing subsidizeFees: true to the quote api (this effectively subsidizes the first time approval). The user still needs to pay for the origin gas fee. For subsidizing fees you’ll need to make sure you have fee sponsorship set up. See the full working example: byo-eoa-permit

ERC-4337 Gasless

Use this approach if your app already uses ERC-4337 smart accounts. ERC-4337 UserOperations normally require gas fees, either paid by the sender or a paymaster. Set all gas fee fields to 0 and submit the handleOps call through the platform’s /execute endpoint; a relayer broadcasts the transaction and pays gas.

originGasOverhead

Pass originGasOverhead: 300000 in the /quote/v2 request. This tells Relay how much additional gas the UserOperation adds over a normal EOA transaction, used during simulation to accurately price the fee.

How it works

1

Get a quote

Call /quote/v2 with originGasOverhead: 300000 to get deposit transaction details
2

Build the UserOperation

Build a UserOperation that batches approve + deposit calls via executeBatch
3

Zero out gas fields

Set maxFeePerGas and maxPriorityFeePerGas to 0
4

Sign the UserOperation

Sign the UserOperation with the account owner key
5

Submit via Relay

Encode an EntryPoint.handleOps() call and submit it via POST /execute with subsidizeFees: true
6

Monitor

Poll /intents/status/v3 until the bridge completes

Fees

You can choose to subsidize origin fees: pass subsidizeFees: true to the execute api executionOptions. You can choose to subsidize destination fees: pass subsidizeFees: true to the quote api. You can choose to subsidize both or none of these. If not subsidized the user pays from the output token. For subsidizing fees you’ll need to make sure you have fee sponsorship set up. See the full working example: 4337-gasless

7702 BatchExecutor

Use this approach when your app controls an embedded or custodial wallet and you want gasless execution with any ERC-20 token — not just permit-compatible ones. This leverages EIP-7702 to delegate the user’s EOA to a BatchExecutor contract (Calibur or similar), enabling atomic approve + deposit in a single call.

originGasOverhead

Pass originGasOverhead: 80000 in the /quote/v2 request. This tells Relay how much additional gas the Calibur execute() wrapper adds over the raw inner calls (EIP-712 signature verification, batch dispatch, nonce check). This is lower than ERC-4337’s 300000 because there’s no EntryPoint or UserOp validation overhead.
The 80000 value is specific to Calibur. Different BatchExecutor contracts may have different gas overheads — adjust this value based on the implementation you use.

How it works

1

Check delegation

Check if the user’s EOA is already delegated to the BatchExecutor via EIP-7702
2

Get a quote

Call /quote/v2 with originGasOverhead: 80000 to get deposit transaction details
3

Build the batch

Build a BatchedCall that atomically combines approve() and deposit()
4

Sign the authorization

Sign an EIP-7702 authorization (off-chain, no gas) delegating the EOA to the BatchExecutor
5

Submit via Relay

Submit via POST /execute targeting the user’s EOA

Fees

You can choose to subsidize origin fees: pass subsidizeFees: true to the execute api executionOptions. You can choose to subsidize destination fees: pass subsidizeFees: true to the quote api. You can choose to subsidize both or none of these. If not subsidized the user pays from the output token. For subsidizing fees you’ll need to make sure you have fee sponsorship set up. See the full working example: 7702-batch-executor

Gasless Swaps on Solana

Use this approach for Solana-origin trades, where permits do not exist. A sponsor wallet your app controls pays the transaction fees and rent, so your users need zero SOL: pass depositFeePayer in the quote, and your app co-signs each transaction as the fee payer. You need a sponsor wallet funded with SOL (your backend holds its key; it never touches the client), a Relay API key, and @solana/web3.js on your backend. Three restrictions from Solana Fee Sponsorship apply to every step below:
  1. depositFeePayer must be a different address from user. The quote API does not enforce this, so your app must.
  2. The sponsor signs the transaction before the user. Signing in the other order lets the user modify the transaction and drain the sponsor.
  3. Do not use depositFeePayer for same-chain Solana swaps with untrusted users. That combination is exploitable.

How it works

1

Get a quote with a sponsor

Call /quote/v2 with depositFeePayer set to your sponsor wallet.
The response’s deposit step carries raw instructions, addressLookupTableAddresses, and a check endpoint for tracking.
2

Assemble the transaction

On your backend, build a versioned transaction from the returned instructions with the sponsor as payerKey (instruction data is hex-encoded).
3

Sign: sponsor first, then user

Sign with the sponsor key on your backend, then hand the partially signed transaction to the client for the user’s signature.
4

Submit and track

Broadcast the signed transaction, then poll the quote’s check endpoint until the request reaches a terminal status.
A failed fill refunds automatically on the origin chain in the origin currency; see Refunds for the conditions.

Fees

depositFeePayer covers the origin transaction only: Solana transaction fees and rent for accounts the deposit creates. It is independent from the destination side and from your App Balance. To also sponsor destination gas or rent, use subsidizeFees and subsidizeRent, which draw from your App Balance instead of the sponsor wallet. Each trade is one Solana deposit transaction, so the sponsor pays one fee per trade. If you also want fills to start before the deposit confirms, see Fast Fill. See Solana Support for chain-specific parameters and transaction-size guidance.