How to access RFQ Liquidity
In About the RFQ System, we covered what is RFQ liquidity and how it is aggregated by 0x. This guide will cover the 3 steps to integrate RFQ liquidity into your application.
Overview
Easily integrate RFQ liquidity in 3 steps:
- Query for an indicative price via
/swap/v1/price
- Fetch a firm quote from Swap API via
/swap/v1/quote
- Submit the transaction to blockchain
⏰ The time difference between each step is non-trivial because we are requesting bespoke quotes from market makers. If a client sits on the RFQ firm quote too long, the probability of the transaction reverting increases. Typically, it's expected that time difference between steps 1 and 2 are minimal. We also recommend setting a max amount of time between steps 2 and 3, 30 seconds is usually a reasonable time before re-asking for a firm quote.
Prefer to see a code demo and watch a video? Checkout the Next.js 0x Demo App and video for best practices implementing indicative pricing and firm quotes.
1. Indicative Pricing
Indicative pricing is used for takers who are querying the prices they could receive. The Swap API will respond to an indicative price with the expected rate of trade between the asset pair specified.
The indicative pricing resource is hosted at /swap/v1/price
and responds with pricing information, but that response does not contain a full 0x order, so it does not constitute a legitimate transaction that can be submitted to the Ethereum network (you must use /quote for this).
In order to receive indicative pricing that includes RFQ liquidity, the request to /swap/v1/price
must include a non-null takerAddress
parameter.
Example Parameters of API Request
A takerAddress
is required for RFQ liquidity. This is the address that will be filling the order.
RFQ liquidity is currently available on Mainnet, Polygon, and Arbitrum via Swap API. Find the API endpoint for different chains here.
https://api.0x.org/swap/v1/price // Request an indicative price
?sellToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 // Sell USDC
&sellAmount=30000000 // Sell amount 30 USDC (6 decimals)
&buyToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE // Buy ETH
&takerAddress=$USER_TAKER_ADDRESS // Address that will make the trade
--header '0x-api-key: [API_KEY]' // Replace with your own API key
Sample cURL Request
curl https://api.0x.org/swap/v1/price?sellToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&sellAmount=30000000&buyToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&takerAddress=0x0A975d7B53F8DA11e64196d53Fb35532fea37E85 --header '0x-api-key: [api-key]'
Example Response
The response to an indicative quote will include a price and a buy/sell amount in base unit amount.
{
"chainId": 1,
"price": "1.468894050625440353",
"grossPrice": "1.47107860234993",
"estimatedPriceImpact": "6.805",
"value": "0",
"gasPrice": "31500000000",
"gas": "116000",
"estimatedGas": "116000",
"protocolFee": "0",
"minimumProtocolFee": "0",
"buyTokenAddress": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"buyAmount": "44066821518763210619",
"grossBuyAmount": "44132358070497900000",
"sellTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"sellAmount": "30000000",
"grossSellAmount": "30000000",
"sources": [
{
"name": "0x",
"proportion": "1"
},
{
"name": "Uniswap",
"proportion": "0"
},
{
"name": "Uniswap_V2",
"proportion": "0"
},
{
"name": "Curve",
"proportion": "0"
},
{
"name": "Balancer",
"proportion": "0"
},
{
"name": "Balancer_V2",
"proportion": "0"
},
{
"name": "BancorV3",
"proportion": "0"
},
{
"name": "SushiSwap",
"proportion": "0"
},
{
"name": "DODO",
"proportion": "0"
},
{
"name": "DODO_V2",
"proportion": "0"
},
{
"name": "CryptoCom",
"proportion": "0"
},
{
"name": "Lido",
"proportion": "0"
},
{
"name": "MakerPsm",
"proportion": "0"
},
{
"name": "KyberDMM",
"proportion": "0"
},
{
"name": "Uniswap_V3",
"proportion": "0"
},
{
"name": "Curve_V2",
"proportion": "0"
},
{
"name": "ShibaSwap",
"proportion": "0"
},
{
"name": "Synapse",
"proportion": "0"
},
{
"name": "Synthetix",
"proportion": "0"
},
{
"name": "Aave_V2",
"proportion": "0"
},
{
"name": "Compound",
"proportion": "0"
},
{
"name": "KyberElastic",
"proportion": "0"
},
{
"name": "Maverick_V1",
"proportion": "0"
},
{
"name": "PancakeSwap_V3",
"proportion": "0"
}
],
"allowanceTarget": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"sellTokenToEthRate": "1808.68027",
"buyTokenToEthRate": "2854.99025193368642365",
"auxiliaryChainData": {},
"fees": {
"zeroExFee": {
"feeType": "volume",
"feeToken": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"feeAmount": "65536551734689381",
"billingType": "on-chain"
}
}
}
2. Firm Quotes
When a taker is ready to actually perform a fill, they will request a firm quote from Swap API. At this point, the taker is making a soft commitment to fill the suggested orders, and understands they may be penalized by the Market Maker if they do not.
The firm quote resource is hosted at /swap/v1/quote
and responds with a full 0x order, which can be submitted to an Ethereum node by the client. Therefore it is expected that the maker has reserved the maker assets required to settle the trade, leaving the order unlikely to revert.
In order to qualify for RFQ liquidity, the request to /swap/v1/quote
must include the following parameter:
- non-null
takerAddress
Best Practices: Note that RFQ quotes from Market Makers are only valid for a short period of time, for example roughly 60s on mainnet. Therefore, we highly recommend adding a mechanism that refreshes the quotes, approximately every 30s to make sure RFQ orders don’t expire. See Matcha.xyz for an example of this in action.
Example Parameters of API Request
A takerAddress
is required for RFQ liquidity. This is the address that will be filling the order.
RFQ liquidity is currently available on Mainnet, Polygon, and Arbitrum via Swap API. Find the API endpoint for different chains here.
https://api.0x.org/swap/v1/quote // Request a firm quote
?sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE // Sell ETH
&sellAmount=100000000000000000 // Sell amount 0.1 ETH (18 decimals)
&buyToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 // Buy USDC
&takerAddress=$USER_TAKER_ADDRESS // Address that will make the trade
--header '0x-api-key: [API_KEY]' // Replace with your own API key
Sample cURL Request
The following example cURL request does not add a fee recipient parameter; however, this cURL request can be easily modified for your own monetization needs. Learn more about monetizing your app here.
curl https://api.0x.org/swap/v1/quote?sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&sellAmount=100000000000000000&buyToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&takerAddress=0x0A975d7B53F8DA11e64196d53Fb35532fea37E85 --header '0x-api-key: [api-key]'
Example Response
{
"chainId": 1,
"price": "1806.95928",
"guaranteedPrice": "1788.86281",
"estimatedPriceImpact": "0.0001",
"to": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"data": "0x415565b0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000aa9968900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e6973776170563300000000000000000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000aadb045000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000419bc000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd000000000000000000000000100000000000000000000000000000000000001100000000000000000000000000000000e48d01911fbcbf1903772de9b84f6632",
"value": "100000000000000000",
"gas": "160254",
"estimatedGas": "160254",
"gasPrice": "26500000000",
"protocolFee": "0",
"minimumProtocolFee": "0",
"buyTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"sellTokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"buyAmount": "180695928",
"sellAmount": "100000000000000000",
"sources": [
{
"name": "0x",
"proportion": "1"
},
{
"name": "Uniswap",
"proportion": "0"
},
{
"name": "Uniswap_V2",
"proportion": "0"
},
{
"name": "Curve",
"proportion": "0"
},
{
"name": "Balancer",
"proportion": "0"
},
{
"name": "Balancer_V2",
"proportion": "0"
},
{
"name": "BancorV3",
"proportion": "0"
},
{
"name": "SushiSwap",
"proportion": "0"
},
{
"name": "DODO",
"proportion": "0"
},
{
"name": "DODO_V2",
"proportion": "0"
},
{
"name": "CryptoCom",
"proportion": "0"
},
{
"name": "Lido",
"proportion": "0"
},
{
"name": "MakerPsm",
"proportion": "0"
},
{
"name": "KyberDMM",
"proportion": "0"
},
{
"name": "Uniswap_V3",
"proportion": "0"
},
{
"name": "Curve_V2",
"proportion": "0"
},
{
"name": "ShibaSwap",
"proportion": "0"
},
{
"name": "Synapse",
"proportion": "0"
},
{
"name": "Synthetix",
"proportion": "0"
},
{
"name": "Aave_V2",
"proportion": "0"
},
{
"name": "Compound",
"proportion": "0"
},
{
"name": "KyberElastic",
"proportion": "0"
},
{
"name": "Maverick_V1",
"proportion": "0"
},
{
"name": "PancakeSwap_V3",
"proportion": "0"
}
],
"orders": [
{
"type": 0,
"source": "Uniswap_V3",
"makerToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"takerToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"makerAmount": "180964660",
"takerAmount": "100000000000000000",
"fillData": {
"router": "0xe592427a0aece92de3edee1f18e0157c05861564",
"path": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"gasUsed": 9627,
"routerVersion": 1
},
"fill": {
"input": "100000000000000000",
"output": "180964660",
"adjustedOutput": "175245854",
"gas": 119252
}
}
],
"allowanceTarget": "0x0000000000000000000000000000000000000000",
"decodedUniqueId": "0xe48d01911fbcbf1903772de9b84f6632",
"sellTokenToEthRate": "1",
"buyTokenToEthRate": "1809.64679",
"fees": {
"zeroExFee": {
"feeType": "volume",
"feeToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"feeAmount": "268732",
"billingType": "on-chain"
}
},
"grossPrice": "1809.6466",
"grossBuyAmount": "180964660",
"grossSellAmount": "100000000000000000",
"auxiliaryChainData": {}
}
3. Submitting the Transaction
The firm quote returned by Swap API is an unsigned Ethereum transaction. In order to submit the transaction to the network, you just need to sign the transaction with your preferred web3 library (wagmi, web3.js, ethers.js) and submit it to the blockchain.
Read more about how to sign and submit a transaction here.
Also, make sure a token allowance has been given for the allowanceTarget
parameter returned by the /swap/v1/quote
response.
Advanced Options
Quote Validation
Whenever a Swap API client specifies a takerAddress
in their /quote
request, the API will validate the quote before returning it to the client, avoiding a number of possible causes for transaction reverts. For more details, see "How does takerAddress
help with catching issues?"
However, given that a takerAddress
is required in order to obtain RFQ liquidity, and given that this requirement subverts the optionality of the quote validation feature, the implementation of RFQ introduced a new query parameter to the /quote
endpoint: skipValidation
. When this parameter is set to true
, quote validation will be skipped. While validating even RFQ quotes is a best-practice recommended default, skipping validation can be useful in certain circumstances, such as when experimenting with a new maker integration deployment or when the takerAddress
refers to a smart contract.
Note for Smart Contract Integrations
One particular circumstance in which it may be necessary to skip quote validation is that in which the takerAddress
refers to a smart contract. In this case, the validation of the quote by the Swap API could fail due to a lack of asset balances in the contract's account. In order to avoid such a validation failure, simply avoid validation, by specifying skipValidation=true
in the query string of your /quote
request.
Excluding Liquidity Sources
When requesting a quote from the Swap API, clients can choose to have the API exclude specific liquidity sources.
At this time, RFQ liquidity is considered by the Swap API to be included within the 0x
/Native
liquidity group. In the API's interface, it's referred to as 0x
, but in the underlying routing logic at the protocol-level it's referred to as Native
. Referencing as either works in the system.
# Example request using the excludedSources=0x flag
https://api.0x.org/swap/v1/quote // Request a firm quote
?sellToken=DAI // Sell DAI
&sellAmount=4000000000000000000000 // Sell amount: 4000 (18 decimal)
&buyToken=ETH // Buy ETH
&takerAddress=0x3bA5De64E24Eea0E974393BeF8a047B58f961c08. // Address that will make the trade
&skipValidation=true // We suggest to set this parameter, if you do not want Swap API to simulate the trade
&feeRecipient=0x46B5BC959e8A754c0256FFF73bF34A52Ad5CdfA9. // Specifies the fee recipient
&buyTokenPercentageFee=0.01 // Pays a 1% fee denominated in WETH to `feeRecipient`
&excludedSources=0x // Excludes RFQ liquidity. excludedSources=Native also works
--header '0x-api-key: [API_KEY]' // Replace with your own API key
Therefore, if a Swap API client intends to access RFQ liquidity, it's important that they not exclude the 0x
or the Native
liquidity sources.
Testing Your RFQ Integration (Recommended)
The best way to ensure that your RFQ integration is working end-to-end (at least, between you and Swap API) is to add the includedSources=0x
flag. A /swap
request with this parameter will:
- raise an error if
takerAddress
is not present - return pricing for only RFQ liquidity
- raise an error if API key is invalid
https://api.0x.org/swap/v1/quote // Request a firm quote
?sellToken=DAI // Sell DAI
&sellAmount=4000000000000000000000 // Sell amount: 4000 (18 decimal)
&buyToken=ETH // Buy ETH
&takerAddress=0x3bA5De64E24Eea0E974393BeF8a047B58f961c08. // Address that will make the trade
&skipValidation=true // We suggest to set this parameter if you do not want Swap API to simulate the trade during testing
&feeRecipient=0x46B5BC959e8A754c0256FFF73bF34A52Ad5CdfA9. // Specifies the fee recipient
&buyTokenPercentageFee=0.01 // Pays a 1% fee denominated in WETH to `feeRecipient`
&includedSources=0x // Ensures only RFQ liquidity is sourced
--header '0x-api-key: [API_KEY]' // Replace with your own API key
Code Examples
- Next.js 0x Demo App (TypeScript) - A Next.js app that shows best practices for implementing indicative pricing and firm quotes
- Video walking through setting up indicative pricing and firm quotes