Overview
Gasless execution lets a user trade when their native balance is zero. Instead of the user sending and paying for the transaction, they sign an authorization off-chain and DZap submits the transaction on their behalf, recovering the gas cost from the trade output in the source token.No native token needed
A user holding only USDC on Arbitrum can trade it with 0 ETH in their wallet.
Trade the full balance
Nothing has to be held back to pay for gas, so 100% of a token can be swapped or bridged.
Same routes, same rates
Gasless uses the normal DZap aggregation. Only the fee line changes.
Works across chains
Available for both same-chain swaps and cross-chain bridges on supported EVM networks.
How a gasless trade works
1
Authorize the token
The user permits the DZap router to move the source token — by signing an EIP-2612 permit, by a one-time ERC-20 allowance, or by a one-time
Permit2 approval.
2
Get a gasless quote
Quotes requested with
gasless: true return routes whose destAmount is already net of the relayer’s gas. The fee itself is
itemized at build time, not quote time — see Fees.3
Sign the intent
The user signs an EIP-712 “user intent” that binds their address, the exact trade data, a nonce and a deadline. This signature is what authorizes
DZap to execute — and only that trade.
4
DZap executes and settles the fee
DZap submits the transaction and pays gas. The gas equivalent is deducted from the output in the source token.
Nothing in this flow gives DZap open-ended access to funds. The intent signature is bound to a single
txId, carries a deadline, and is consumed by
a per-user nonce, so it cannot be replayed.Fees
The gasless fee is charged in the source token and is deducted from what the user receives. Both the quote and the build return it, in different fields and at different accuracy:
Show the quote’s
protocolFee while the user is still choosing, and re-read executorFee from the
build for the confirmation screen. Both carry the same shape:
Fee entry
amount is in the source token’s smallest unit; amountUSD is the same fee priced in dollars.
included: true means it is already reflected in the quoted output — do not subtract it again.
For a multi-token trade, every
srcToken in data[] must be gasless-listed — one unlisted
source token makes the whole request ineligible. Check each against the list before requesting a
gasless quote. The $1 minimum is applied per pair, not to the basket total.
Minimum trade size
The fee has to be covered by the trade itself, so the API enforces a $1 minimum per pair for gasless. Below it, the pair returns an explicit error instead of routes:status/message rather than inferring the floor from an empty route list — the
threshold is server-side and can move with gas prices.
Gasless or regular?
Prerequisites
Every source token must support gasless
Every source token must support gasless
Gasless is enabled per chain and per token, and the constraint applies to the source side. Every
srcToken in data[] must be on the
gasless token list — if one is not, the request is not eligible for gasless. Destination tokens are unconstrained:
you can trade into any token DZap routes to on the destination chain.In practice that means one constrained token selector, not two — filter the “from” list against the gasless list and leave the “to” list alone.Check the list before offering gasless. Do not infer support from the quote: /v1/quotes and /v1/buildTx still return gasless: true routes for
an unlisted source token, so a failed lookup against the list is your only reliable pre-flight signal.The source token must be an ERC-20
The source token must be an ERC-20
Native tokens (ETH, MATIC, …) cannot be traded gaslessly: moving native value requires the user to send the transaction, which is exactly what
gasless avoids. The gasless list contains only ERC-20 tokens.
EVM chains only
EVM chains only
Gasless runs on DZap’s v2 EVM router. Solana, Bitcoin, Sui, Aptos, Ton and HyperLiquid trades are not gasless.
The trade must clear the $1 minimum
The trade must clear the $1 minimum
See Fees. Below it the pair returns a
status: "error" with an explicit message.Use an API key during development
Use an API key during development
The gasless and build endpoints are rate-limited per IP without a key, and the limits are low enough to hit while iterating —
/v1/buildTx allows
5 requests per 60s unauthenticated. A 429 body is an error object, not a token map, so code that assumes the happy shape renders “no gasless
tokens” and looks like an unsupported chain.Send your key as x-api-key and branch on 429 explicitly. See Rate Limits.Step 1 — Check gasless support
Two endpoints expose the gasless support matrix.GET /v1/token/gasless returns tokens grouped by chain ID, then keyed by checksummed token
address. GET /v1/token/gasless/{chainId} returns just the inner map for one chain.
Response (trimmed)
permit field tells you which authorization modes the token supports and therefore which
gasless flow to use. balance and price are not populated by this endpoint — use
getAllTokens / getTokenDetails for those.
With the SDK
Step 2 — Choose an authorization mode
DZap can only move the source token if it is authorized to. There are three ways to do that, and the choice determines whether the user needs gas now or not at all.“Needs gas” applies only to the one-time setup transaction. Once the allowance or Permit2 approval exists, every subsequent gasless trade costs the
user zero native token.
Why
sign() and approve() take different enums. sign() takes a PermitTypes value —
a signature scheme. approve() and getAllowance() take an ApprovalModes value — an
on-chain approval target (Default → the router, anything else → Permit2).ApprovalModes deliberately has no EIP2612Permit member: an EIP-2612 permit is a signature
and never approves anything on-chain, so there is nothing for approve() to do. The asymmetry
is intentional, not a typo.Step 3 — Get a gasless quote
Passgasless: true to the quote request. Quotes are returned keyed by token pair
({srcChainId}_{srcToken}-{destChainId}_{destToken}), which getTokensPairKey builds for you.
An empty
quoteRates does not mean “not gasless”. A source token that is not on the gasless list
still returns gasless: true routes here — the API does not gate quotes on the list. The two
cases you can actually distinguish are:pair.status === 'error'with amessage— below the $1 minimum.- Token absent from the gasless list (step 1) — check this yourself before offering gasless.
trade() and let the user pay gas normally.The
$1 minimum applies per pair. In a multi-token request one pair can fail this check while
the others quote normally — check status on each pair, not just the first.Step 4 — Execute
tradeGasless() does the whole tail of the flow in one call: it builds the gasless transaction,
collects the intent signature from the wallet, and submits it to DZap for execution. The mode is
selected by what you pass in.
- EIP-2612 permit
- ERC-20 allowance
- Permit2
No on-chain setup. The user signs the token permit, then the intent — two wallet prompts, zero
gas.
sign() returns the tokens array with permitData filled in per token. Check
permit.eip2612.supported from step 1 first — signing an EIP-2612 permit for a token that
does not implement it throws.Driving a progress UI
txnStatusCallback receives TxnStatus values. Of the eight members, these are the ones a gasless
trade emits, and they map directly onto what you should show:
In EIP-2612 mode
pendingWalletConfirmation fires twice — once for the token permit and once
for the intent. Do not treat the second one as a stuck state.
Reusing a build response
tradeGasless() builds the transaction internally. If you already built it — to show a
confirmation screen with the fee, for instance — pass it back as txnData to skip the rebuild:
GaslessBaseParamsResponse:
status, gasless and onlySwapData are literal types, so a gasless build narrows cleanly — no
cast needed. fees.executorFee is the authoritative fee described in Fees.
What invalidates a build. The intent signature the build is bound to expires after
SignatureExpiryInSecs — 1800 seconds (30 minutes), exported from the SDK. Rebuild if:
- the deadline has passed, or
- the user’s router nonce was consumed by another trade in the meantime, or
- you are switching authorization mode — adding
permitDataafter building changes the request, so the earlier build no longer matches. Rebuild on that path.
Step 5 — Track the transaction
tradeGasless() resolves with the hash of the transaction DZap submitted. Track it exactly like a
regular trade:
STATUS_RESPONSE is exported — compare against its members rather than hand-written strings.
status.status is one of exactly five values:
Do not confuse
STATUS_RESPONSE with the separately exported STATUS constant — that one is an
unrelated enum with lowercase members (pending, success, …) and will never match a trade status.PENDING.
Complete example
Every block above depends on identifiers from earlier steps. This one is self-contained — copy it whole. It covers the EIP-2612 path, the one that works for a wallet with zero native balance.Advanced — the REST flow
Use this if you are not using the SDK. It is the same sequencetradeGasless() performs.
1
POST /v1/quotes
Body as in step 3, with
"gasless": true. Pick a route.2
POST /v1/buildTx
Body: the trade request with
"gasless": true, the chosen protocol, sender, refundee,
recipient. The gasless response is not raw calldata — it is the set of hashes you must sign:fees.executorFee is the authoritative gasless fee — show this on the confirmation screen
rather than the quote’s estimate.3
Sign the user intent (EIP-712)
Read the user’s current nonce from the router. The router address for a chain comes from
DomainPrimary type — chosen by what the build response contains:
GET /v1/chains → contracts.router:txId is bytes32, user is address, everything else is uint256/bytes32 as named.
deadline is a unix timestamp. The SDK uses SignatureExpiryInSecs — 1800 seconds (30
minutes) from signing. Match it unless you have a reason not to; a shorter deadline is safer,
a longer one widens the replay window.For the Permit2 mode you sign a PermitBatchWitnessTransferFrom instead, with the same fields
carried as the witness, against the Permit2 domain.4
POST /v1/gasless/executeTx
EIP-2612 or allowance mode:Permit2 mode:The response carries
status and the txnHash DZap submitted. In allowance mode the per-token
permit is the default empty permit — ABI-encoded (uint8 mode, bytes data) with
mode = 0 (EIP-2612) and data = 0x.Error handling
Reference
Endpoints
Base URLs:
https://api.dzap.io (production), https://staging.dzap.io (staging). Send your API
key as the x-api-key header — see Rate Limits for the unauthenticated caps.