Skip to main content
Version: 2.0

Gasless API - Technical Appendix

Learn how to sign the trade & approval objects, split signatures, submit, and compute the trade hash for a gasless trade

The following is the typical flow when using Gasless API. This guide covers how to complete steps 3 - 4.

  1. Get an indicative price using /gasless/price
  2. Get a firm quote using /gasless/quote
  3. Submit the transaction using /gasless/submit
    1. Sign the gasless approval object (if applicable)
    2. Sign the trade object
    3. Split the signatures
    4. Package split signed objects into a format that can be POST to /gasless/submit
    5. Compute trade hash
  4. Check the trade status using /gasless/status/{tradeHash}
tip

Checkout the Gasless API Runnable Headless Example to see these steps in action

Signing objects

To take advantage of gaslesses approvals and gasless trades, user must sign the approval.eip712 and the trade.eip712 objects returned by /quote. These objects contain everything (domain, types, primaryType, message) needed when signing these objects .

There are different EIP-712 signing strategies if you are user facing wallet that shows the users the details of what they are signing. Some commonly used tools for this include:

Sign gasless approval object

If a token supports gasless approvals, the /quote response will return an “approval” object which contains the necessary information to process a gasless approval, see below:

 "approval":{
"type": "permit", // this is approval.type from the /quote endpoint
"hash": "0xf3849ebcd806e518f2d3457b76d31ccf41be07fe64f0a25bbe798f1b9edde872",
"eip712": {
"types": {/* this is approval.eip712.types from the /quote endpoint */},
"domain": {/* this is approval.eip712.domain from the /quote endpoint */},
"message": {/* this is approval.eip712.message from the /quote endpoint */},
"primaryType": "Permit",
}
}
}

Keep in mind that the domain field of this object must follow a strict ordering as specified in EIP-712. The approval.eip712 object will present the correct ordering, but make sure that this ordering is preserved when obtaining the signature:

  • name , version, chainId, verifyingContract, salt
    • Contracts may not utilize all of these fields (i.e. one or more may be missing), but if they are present, they must be in this order
  • Any other field must follow in alphabetical order

Here is an example to sign it using viem's signTypedData:

// Sign the data returned by approval.712 in the quote response
async function signApprovalObject(): Promise<any> {
// Logic to sign approval object
const approvalSignature = await client.signTypedData({
types: quote.approval.eip712.types,
domain: quote.approval.eip712.domain,
message: quote.approval.eip712.message,
primaryType: quote.approval.eip712.primaryType,
});
return approvalSignature;
}

Sign trade object

Similar to gasless approval, to submit a trade, you must have your user sign trade.eip712 object returned at the time of the /quote. All the instructions & caveats are the same as previous section.

tip

See example code to sign trade object

Here is an example to sign it using viem's signTypedData:

// Sign the data returned by trade.712 in the quote response
async function signTradeObject(): Promise<any> {
// Logic to sign trade object
const tradeSignature = await client.signTypedData({
types: quote.trade.eip712.types,
domain: quote.trade.eip712.domain,
message: quote.trade.eip712.message,
primaryType: quote.trade.eip712.primaryType,
});
return tradeSignature;
}

Split signatures

Once signed, the signature needs to be split to its individual components (v, r, s) and to be formatted in an object that can be POST to /submit (see object format here).

tip

Example code showing how to implement a split signature function. This is a variation of this splitSignature implementation.

Example code using split signature to split approval and trade signatures

POST the split signatures to /submit

Example request

Once the signatures have been split, in order to POST to /submit, approval and trade objects must be formatted to contain the following key/value pairs:

curl -X POST '<https://api.0x.org/gasless/submit>' --header '0x-api-key: <API_KEY>' --header '0x-version: v2' --data '{
"trade": {
"type": "settler_metatransaction", // this is trade.type from the /quote endpoint
"eip712": { /* this is trade.eip712 from the /quote endpoint */ },
"signature": {
"v": 27,
"r": "0xeaac9ddbbf9b251a384eb4a545a2800a6d7aca4ceb5e5a3154ddd0d2923533d2",
"s": "0x601deadfd33bd8b0ad35468613eabcac0a250a7a32035e261a13e2dcbc462e49",
"signatureType": 2
}
},
"approval":{
"type": "permit", // this is approval.type from the /quote endpoint
"eip712": {/* this is approval.eip712 from the /quote endpoint */},
"signature": {
"v": 28,
"r": "0x55c26901285d5cb4d9d1ebb2828122fd6c334b343901944d34a810b3d2d79773",
"s": "0x20854d829e3118c3f780228ca83fac6154060328a90d2a21267c9f7d1ae9e53b",
"signatureType": 2
}
}
}'

Here is an example code snippet of how to submit it using JavaScript:

 // Make a POST request to submit trade with split signed trade object (and approval object if available)
async function submitTrade(
tradeDataToSubmit: any,
approvalDataToSubmit: any
): Promise<void> {
try {
let successfulTradeHash;
const requestBody: any = {
trade: tradeDataToSubmit,
chainId: client.chain.id,
};
if (approvalDataToSubmit) {
requestBody.approval = approvalDataToSubmit;
}
const response = await fetch("https://api.0x.org/gasless/submit", {
method: "POST",
headers: {
"0x-api-key": process.env.ZERO_EX_API_KEY as string,
"0x-version": process.env.ZERO_EX_API_VERSION as string,
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
const data = await response.json();
successfulTradeHash = data.tradeHash;
console.log("#️⃣ tradeHash: ", successfulTradeHash);
return successfulTradeHash;
} catch (error) {
console.error("Error submitting the gasless swap", error);
}
}

Example response

If the submitted trade is successful, and object with type, tradeHash, and zid are returned.

{
"tradeHash": "0xcb3285b35c024fca76037bea9ea4cb68645fed3bdd84030956577de2f1592aa9",
"type": "settler_metatransaction",
"zid": "0x111111111111111111111111"
}

Status Code

  • 201 if successful
  • 400:
    • If the trade is too close to expiration time.
    • If the signature in the payload is invalid.
    • If the balance / allowance of the taker is less than the trade amount.
    • (otc only) If the trade has been outstanding for too long.
    • (otc only) If the balance / allowance of the market maker selected to settle the trade is less than the trade amount (very unlikely).
    • If the query params are not able to pass validation.
  • 429 if there is already a trade associated with a taker address and a taker token that's not been settled by our relayers yet. For example, if address A already has a USDC -> WETH trade submitted and it has not settled yet, then a subsequent /submit call with address A and USDC -> * trade will fail with 429. The taker is, however, allowed to submit other trades with a different taker token.
  • 500 if there is an internal server error.

Note for go-ethereum

  • If you're using go-ethereum, for domain, make sure you order the fields in the exact same order as specified in https://eips.ethereum.org/EIPS/eip-712 since go-ethereum does not enforce ordering. Also, make sure you skipped fields that are absent.
- string name: the user readable name of signing domain, i.e. the name of the DApp or the protocol.
- string version: the current major version of the signing domain. Signatures from different - versions are not compatible.
- uint256 chainId: the EIP-155 chain id. The user-agent should refuse signing if it does not match the currently active chain.
- address verifyingContract: the address of the contract that will verify the signature. The user-agent may do contract specific phishing prevention.
- bytes32 salt: an disambiguating salt for the protocol. This can be used as a domain separator of last resort.

The EIP712Domain fields should be the order as above, skipping any absent fields
  • If you're using ethers v6 (v5 and v4 are fine), there is an issue for signing EIP-712 object. Make sure you updated ethers version to >= 6.3.0.