This page explains how market makers can interact with the Liquorice RFQ WebSocket API using their own inventory
1. Getting started
Connecting to maker API
Production: wss://api.liquorice.tech/v1/maker/ws
Include the following headers with the WebSocket request
maker - name of the Market Maker
authorization - authorization token
The WebSocket server sends Ping message every 30 seconds.
2. Managing token approvals
Standard approvals
Token approval must be granted solely to the Liquorice Balance Manager smart contract address. Giving standard approval is the most gas efficient way to trade.
Important note: providing approval to any other Liquorice address outside of Balance Manager may compromise the security of your funds, as it does not guarantee the same level of protection. Approvals should be given strictly to the Balance Manager address.
To use Permit2, first grant standard token approval to address: 0x000000000022d473030f116ddee9f6b43ac78ba3
3. Receiving RFQ
When a trader or intent system requests a quote, Liquorice forwards the request to market makers using the following format:
{
messageType: "rfq";
message: {
/// Chain ID (e.g for ArbitrumOne chainId = 42161)
chainId: number;
/// Name of the solver requesting the quote
solver: string;type
/// UUID of the originating solver RFQ
solverRfqId: string;
/// UUID of the RFQ
rfqId: string;
/// Hex-encoded 32-byte nonce
nonce: string;
/// Address of token to be sent by trader
baseToken: string;
/// Address of the token to be received by trader
quoteToken: string;
/// Address of the account receiving quoteToken
trader: string;
/// Address of the account sending baseToken
effectiveTrader: string;
/// Note: Exactly one of the following two fields must be present
/// Amount of baseToken with up to 18 decimal places, e.g. 100000000 (1 WBTC)
baseTokenAmount?: string;
/// Amount of quoteToken with up to 18 decimal places
quoteTokenAmount?: string;
};
}
The RFQ will include either baseTokenAmount or quoteTokenAmount, but not both.
When baseTokenAmount is specified, the trader (order initiator) is requesting the amount of quoteToken they would receive in exchange for a specific amount of baseToken.
When quoteTokenAmount is specified, the trader (order initiator) is requesting the amount of baseToken they need to provide to receive a specific amount of quoteToken.
4. Providing Quote
Send the quote response through the same WebSocket connection using the following format:
{
messageType: "rfqQuote",
message: {
/// UUID of the RFQ (must match rfqId of the received RFQ message)
rfqId: string,
/// Quote levels
levels: [{...}]
}
}
Market makers can provide multiple quote levels with different amounts, but the quoted amounts must not exceed those specified in the RFQ.
Example
For an RFQ where:
baseToken = ETH
quoteToken = USDT
baseTokenAmount = 1 ETH
The resulting quote may contain these levels:
Level 0: Exchange 1 ETH for 3,000 USDT
Level 1: Exchange 0.5 ETH for 1,502 USDT
Level 2: Exchange 0.1 ETH for 310 USDT
Quote level Lite
Use this type of quote level for simple and gas-efficient swaps.
Note: supports only Standard approvals
{
/// Type identifier for the quote level
type: "lite",
/// Quote level expiration UNIX timestamp (seconds)
expiry: number,
/// Address of the LiquoriceSettlement contract
settlementContract: string,
/// Address of the signign account.
/// The quoteToken will be transferred from this address during the settlement
signer: string,
/// Optional address of the baseToken recipieint
/// If omitted, the signer address will be used as the recipient
recipient?: string,
/// Address of token to be sent by trader
baseToken: string,
/// Address of the token to be received by trader
quoteToken: string,
/// Amount of baseToken with up to 18 decimal places, e.g. 100000000 (1 WBTC)
baseTokenAmount: string,
/// Amount of quoteToken with up to 18 decimal places
quoteTokenAmount: string,
/// Minimum amount of quoteToken to receive for partial fills,
/// with up to 18 decimal places
minQuoteTokenAmount: string,
/// 0x-prefixed signature for this quote level
signature: string,
}
Signature schema
The following Solidity code demonstrates how to create an EIP-712 signature by hashing RFQ and quote level data.
To implement this in your preferred programming language, port the code below and populate values from the corresponding RFQ and quote level objects.
// RFQ part of the hash
{
"baseToken": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
"baseTokenAmount": "1",
"chainId": 42161,
"effectiveTrader": "0x033F42e758cEbEbC70Ee147F56ff92C9f7CA45F4",
"nonce": "0000000000000000000000000000000000000000000000000000000000000000",
"quoteToken": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f",
"rfqId": "c99d2e3f-702b-49c9-8bb8-43775770f2f3",
"trader": "0x48426Ef27C3555D44DACDD647D8f9bd0A7C06155"
}
// Quote level part of the hash
{
"baseToken": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
"baseTokenAmount": "1",
"expiry": 1715787259,
"minQuoteTokenAmount": "1",
"quoteToken": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f",
"quoteTokenAmount": "2",
"recipient": "0x033F42e758cEbEbC70Ee147F56ff92C9f7CA45F4",
"settlementContract": "0x48426Ef27C3555D44DACDD647D8f9bd0A7C06155",
}
Quote level Extended
Use this quote level type for customizations that require hooks and interactions.
It supports both Standard and Permit2 approvals.
{
/// Type identifier for the quote level
type: "ext",
/// Quote level expiration UNIX timestamp (seconds)
expiry: number,
/// Address of the LiquoriceSettlement contract
settlementContract: string,
/// Address of the signign account.
/// The quoteToken will be transferred from this address during the settlement
signer: string,
/// Optional address of the baseToken recipieint
/// If omitted, system will assume that `signer` is the baseToken recipient
recipient?: string,
/// Data of token to be sent by trader
baseTokenData: {
/// Token address
address: string,
/// Total amount of baseToken to take from effectiveTrader,
/// with up to 18 decimal places
amount: string,
/// Part of the amount to transfer to the recipieint
toRecipient?: string,
/// Part of the amount to repay token debt with (TBD)
toRepay?: string,
/// Part of the amount to supply as collateral (TBD)
toSupply?: string,
},
/// Data of token to be received by trader
quoteTokenData: {
/// Token address
address: string,
/// Total amount of quoteToken to take from signer,
/// with up to 18 decimal places
amount: string,
/// Minimum amount of quoteToken to take for partial fills,
minAmount: string,
/// Part of the amount to be transferred from `signer`,
toTrader?: string,
/// Part of the amount to be filled by withdrawing collateral (TBD)
toWithdraw?: string,
/// Part of the amount to be filled by borrowing (TBD)
toBorrow?: string,
},
/// Token approval type: "Standard" or "Permit2".
/// If omitted, standard approval will be used
approvalType?: string
/// 0x-prefixed signature for this quote level
signature: string,
}
Signature schema
The following Solidity code demonstrates how to create an EIP-712 signature by hashing RFQ and quote level data.
To implement this in your preferred programming language, port the code below and populate values from the corresponding RFQ and quote level objects.
Note: this part of the system is work in progress. For general overview please visit following page.
5. Getting info on settled trades
When a trade executes on-chain, the Liquorice settlement contract emits a TraderOrder event containing information about transferred amounts. The event reflects actual token transfers, including cases of partial fills, and always shows the final amounts transferred on-chain.