***
title: Technical Appendix
description: >-
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 below**.
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}`
Checkout the [Gasless API Runnable Headless Example](https://github.com/0xProject/0x-examples/tree/main/gasless-v2-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`](/api-reference/openapi-yaml/gasless/getquote). 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:
* integrating with MetaMask (via [`signTypedData_v4`](https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4))
* viem's [signTypedData](https://viem.sh/docs/actions/wallet/signTypedData.html)
* wagmi's [signTypedData](https://wagmi.sh/core/api/actions/signTypedData)
### Sign gasless approval object
If a token supports [gasless approvals](/docs/gasless-api/guides/understanding-gasless-api#gasless-approvals), the `/quote` response will return an “approval” object which contains the necessary information to process a gasless approval, see below:
```json
"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
See [example code to sign approval object](https://github.com/0xProject/0x-examples/blob/main/gasless-v2-headless-example/index.ts#L170-L180)
Here is an example to sign it using viem's [signTypedData](https://viem.sh/docs/actions/wallet/signTypedData.html):
```js
// Sign the data returned by approval.712 in the quote response
async function signApprovalObject(): Promise {
// 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.
See example code to [sign trade object](https://github.com/0xProject/0x-examples/blob/main/gasless-v2-headless-example/index.ts#L158-L168)
Here is an example to sign it using viem's [signTypedData](https://viem.sh/docs/actions/wallet/signTypedData.html):
```js
// Sign the data returned by trade.712 in the quote response
async function signTradeObject(): Promise {
// 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](/api-reference#tag/Gasless/operation/gasless::submit)).
Example code showing how to implement a [split signature function](https://github.com/0xProject/0x-examples/blob/main/gasless-v2-headless-example/utils/signature.ts). This is a variation of this [`splitSignature`](https://github.com/wevm/viem/discussions/458#discussioncomment-5842564) implementation.
Example code using split signature to [split approval and trade signatures](https://github.com/0xProject/0x-examples/blob/main/gasless-v2-headless-example/index.ts#L207-L234)
## 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:
```bash
curl -X POST '' --header '0x-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
}
}
}'
```
See [example code to POST split signatures to submit](https://github.com/0xProject/0x-examples/blob/main/gasless-v2-headless-example/index.ts#L236-L265)
Here is an example code snippet of how to submit it using JavaScript:
```js
// 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 {
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.
```bash
{
"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](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 App 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](https://github.com/ethers-io/ethers.js/discussions/3873) for signing EIP-712 object. Make sure you updated `ethers` version to `>= 6.3.0`.