Basic Market Making API
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 Makerauthorization
- 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.
Permit2
Liquorice integrates with the Uniswap Permit2 contract.
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;
/// Optional metadata that provides context about the intent origin
intentMetadata?: {
source: 'cow_protocol';
content: {
/// CoW auction ID
auctionId: number;
}
}
};
}
The RFQ will include either
baseTokenAmount
orquoteTokenAmount
, but not both.When
baseTokenAmount
is specified, thetrader
(order initiator) is requesting the amount ofquoteToken
they would receive in exchange for a specific amount ofbaseToken
.When
quoteTokenAmount
is specified, thetrader
(order initiator) is requesting the amount ofbaseToken
they need to provide to receive a specific amount ofquoteToken
.
intentMetadata
- For the moment, only CoW Protocol is supported. If this parameter is absent, it means that the quote is requested for another intent system.
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,
}
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 LendingPool contract
/// This field is optional and must be provided only when quote
/// presumes interaction with the lending pool
lendingPoolContract?: 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,
}
Using lending pools
Liquorice allows market makers to settle trades using borrowed funds, provided they have posted collateral equal to at least 25% of the quoted amount.
Consider the following example: a market maker receives an RFQ where a trader intends to exchange WETH for USDC. If the market maker does not have USDC in their inventory, they can borrow it from the lending pool—assuming they have supplied collateral (in any token) worth at least 25% of the quoted USDC amount.
For instance, if the quote is to exchange 0.3 WETH for 1,000 USDC, the market maker can post 250 USDT as collateral and borrow 1,000 USDC from the pool.
When the trade is settled, the 0.3 WETH will be added to the locked collateral alongside the market maker’s initially supplied 250 USDT. As a result, the market maker’s position in the pool will consist of 0.3 WETH and 250 USDT as locked collateral, with 1,000 USDC as outstanding debt.
To settle a trade using borrowed funds, the market maker must use the Extended Quote level and specify token amounts in the toSupply
field of baseTokenData
and the toBorrow
field of quoteTokenData
.
baseTokenData.toSupply
: the amount (e.g., 0.3 WETH) that the trader will supply and which will be added to the locked collateral on behalf of the market maker.quoteTokenData.toBorrow
: the amount (e.g., 1,000 USDC) to be borrowed on behalf of the market maker and sent to the trader.
The system will automatically generate the transaction payload and forward it to the origin of the intent.
To withdraw the 0.3 WETH collateral, the market maker must first repay a sufficient portion of the 1,000 USDC debt.
5. Getting quote summary
Use the following endpoint to retrieve an extended information for the provided quote. Quote summary includes information such as originating solver RFQ and resulting solver quote with the competing quote levels from other market makers, and settlement result if the resulting solver quote was used to settle the trade on-chain.
URL: https://api.liquorice.tech/v1/maker/quote-summaries/{rfq-id}
Method: GET
6. 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.
event TradeOrder(
string indexed rfqId,
address trader,
address effectiveTrader,
address baseToken,
address quoteToken,
uint256 baseTokenAmount,
uint256 quoteTokenAmount,
address recipient
);
Last updated