This PR mainly provides a capability for limit order. When users are not satisfied with the current swap price in AMM pool, a limit order can help to set up a transaction trigger for the future. Currently, the contract FlamingoSwapOrderBook is independent but can be regarded as an aid for FlamingoSwapPair.
General Design
FlamingoSwapOrderBook defines a struct OrderBook and a struct LimitOrder to represent the trade pairs and order requirements, the former are indexed with a pairKey and the latter are indexed with an id;
Each OrderBook represents a trade pair, and contains two Linked Lists that manage buy orders and sell orders. The buy order with the highest price and the sell order with the lowest price are always at the first of the queue. Their prices also represent the market prices, the former is MarketSellPrice and the latter is MarketBuyPrice;
For every LimitOrder, the expected payment can be calculated as quoteAmount = baseAmount * limitOrder.price / 10^orderBook.quoteDecimals. For buyers, they deposit quoteAmountquoteToken, wait for an ideal baseAmount and finally receive baseToken, but for sellers, they deposit baseAmountbaseToken, wait for an ideal quoteAmount, and finally receive quoteToken;
Every new LimitOrder is always first used to trade with existing orders in queue, so that the MarketBuyPrice is always higher than the MarketSellPrice and all the orders in queue have no conflict.
This contract can be future invoked from FlamingoSwapRouter so that we will have a price aggregator which consists of a SwapBook and several SwapPairs.
AddLimitOrder(tokenA, tokenB, maker, isBuy, price, amount) - Trade with existing orders and create a new limit order if necessary, returns an order id, 0.15% takerFee and 0.15% makerFee of dealt part to fundAddress;
DealMarketOrder(tokenA, tokenB, taker, isBuy, price, amount) - Only trade with existing orders, returns a left amount, 0.15% takerFee and 0.15% makerFee to fundAddress;
CancelOrder(tokenA, tokenB, isBuy, id) - Cancel a limit order with its id;
GetFirstNOrders(tokenA, tokenB, isBuy, n) - Get the first n orders of the buy/sell queue;
GetTotalTradable(tokenA, tokenB, isBuy, price) - Get the total amount of tradable tokens above/below expected price, as well as the equivalent payment;
MatchOrder(tokenA, tokenB, isBuy, price, amount) - Try match an order without real payment, returns the left amount and the equivalent payment;
GetMarketPrice(tokenA, tokenB, isBuy) - Get the price of the first buy/sell order in queue;
GetNextPrice(tokenA, tokenB, isBuy, price) - Get the next price of the provided one;
GetBaseToken(tokenA, tokenB) - Get the baseToken of the trade pair;
GetQuoteToken(tokenA, tokenB) - Get the quoteToken of the trade pair;
GetQuoteDecimals(tokenA, tokenB) - Get the quoteDecimals of the trade pair.
Summary
This PR mainly provides a capability for limit order. When users are not satisfied with the current swap price in AMM pool, a limit order can help to set up a transaction trigger for the future. Currently, the contract FlamingoSwapOrderBook is independent but can be regarded as an aid for FlamingoSwapPair.
General Design
OrderBook
and a structLimitOrder
to represent the trade pairs and order requirements, the former are indexed with apairKey
and the latter are indexed with anid
;OrderBook
represents a trade pair, and contains two Linked Lists that manage buy orders and sell orders. The buy order with the highest price and the sell order with the lowest price are always at the first of the queue. Their prices also represent the market prices, the former isMarketSellPrice
and the latter isMarketBuyPrice
;LimitOrder
, the expected payment can be calculated asquoteAmount = baseAmount * limitOrder.price / 10^orderBook.quoteDecimals
. For buyers, they depositquoteAmount
quoteToken
, wait for an idealbaseAmount
and finally receivebaseToken
, but for sellers, they depositbaseAmount
baseToken
, wait for an idealquoteAmount
, and finally receivequoteToken
;LimitOrder
is always first used to trade with existing orders in queue, so that theMarketBuyPrice
is always higher than theMarketSellPrice
and all the orders in queue have no conflict.Code Implementation
OrderBook (DEX like API):
AddLimitOrder(tokenA, tokenB, maker, isBuy, price, amount)
- Trade with existing orders and create a new limit order if necessary, returns an orderid
, 0.15%takerFee
and 0.15%makerFee
of dealt part tofundAddress
;DealMarketOrder(tokenA, tokenB, taker, isBuy, price, amount)
- Only trade with existing orders, returns a leftamount
, 0.15%takerFee
and 0.15%makerFee
tofundAddress
;CancelOrder(tokenA, tokenB, isBuy, id)
- Cancel a limit order with itsid
;GetFirstNOrders(tokenA, tokenB, isBuy, n)
- Get the firstn
orders of the buy/sell queue;GetTotalTradable(tokenA, tokenB, isBuy, price)
- Get the totalamount
of tradable tokens above/below expected price, as well as the equivalentpayment
;MatchOrder(tokenA, tokenB, isBuy, price, amount)
- Try match an order without real payment, returns the leftamount
and the equivalentpayment
;GetMarketPrice(tokenA, tokenB, isBuy)
- Get theprice
of the first buy/sell order in queue;GetNextPrice(tokenA, tokenB, isBuy, price)
- Get the nextprice
of the provided one;GetBaseToken(tokenA, tokenB)
- Get thebaseToken
of the trade pair;GetQuoteToken(tokenA, tokenB)
- Get thequoteToken
of the trade pair;GetQuoteDecimals(tokenA, tokenB)
- Get thequoteDecimals
of the trade pair.A simple price aggregator can be found at txhsl/amm-price-aggregator.OrderBook (AMM like API):
MatchQuote(tokenA, tokenB, isBuy, price, quoteAmount)
- Try match an order based on how many quote tokens to spend;GetAmountOut(tokenFrom, tokenTo, startPrice, endPrice, amountIn)
- Calculate like AMMGetAmountOut
;GetAmountIn(tokenFrom, tokenTo, startPrice, endPrice, amountOut)
- Calculate like AMMGetAmountIn
.SwapAggregator (Price Aggregator):
GetAMMPrice(reverseBase, reverseQuote, quoteDecimals)
- Calculate AMM price for comparison;GetAMMAmountInTillPrice(isBuy, price, quoteDecimals, reserveIn, reserveOut)
- Calculate theamountIn
to swap AMM to expected position;GetAmountIn
andGetAmountOut
- Compare price and get allamountIn
andamountOut
for later swap;GetAmountsIn
andGetAmountsOut
- Update;SwapTokenOutForTokenIn
andSwapTokenInForTokenOut
- Update;SwapAMMTillPrice(sender, amountInMax, amountOutMin, tokenIn, tokenOut, isBuy, price, quoteDecimals, deadLine)
;SwapWithOrderBook(tokenIn, tokenOut, amountToBook, amountToPool, amountOutBook, amountOutPool, bookDealPrice)
- Do swap for each step.Note
RequestTransfer
in master branch is used here as well;