flamingo-finance / flamingo-contract-swap

7 stars 5 forks source link

Orderbook #25

Closed txhsl closed 2 years ago

txhsl commented 2 years ago

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

  1. 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;
  2. 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;
  3. For every LimitOrder, the expected payment can be calculated as quoteAmount = baseAmount * limitOrder.price / 10^orderBook.quoteDecimals. For buyers, they deposit quoteAmount quoteToken, wait for an ideal baseAmount and finally receive baseToken, but for sellers, they deposit baseAmount baseToken, wait for an ideal quoteAmount, and finally receive quoteToken;
  4. 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.
  5. This contract can be future invoked from FlamingoSwapRouter so that we will have a price aggregator which consists of a SwapBook and several SwapPairs.

Code Implementation

OrderBook (DEX like API):

  1. 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;
  2. 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;
  3. CancelOrder(tokenA, tokenB, isBuy, id) - Cancel a limit order with its id;
  4. GetFirstNOrders(tokenA, tokenB, isBuy, n) - Get the first n orders of the buy/sell queue;
  5. GetTotalTradable(tokenA, tokenB, isBuy, price) - Get the total amount of tradable tokens above/below expected price, as well as the equivalent payment;
  6. MatchOrder(tokenA, tokenB, isBuy, price, amount) - Try match an order without real payment, returns the left amount and the equivalent payment;
  7. GetMarketPrice(tokenA, tokenB, isBuy) - Get the price of the first buy/sell order in queue;
  8. GetNextPrice(tokenA, tokenB, isBuy, price) - Get the next price of the provided one;
  9. GetBaseToken(tokenA, tokenB) - Get the baseToken of the trade pair;
  10. GetQuoteToken(tokenA, tokenB) - Get the quoteToken of the trade pair;
  11. GetQuoteDecimals(tokenA, tokenB) - Get the quoteDecimals of the trade pair.
  12. A simple price aggregator can be found at txhsl/amm-price-aggregator.

OrderBook (AMM like API):

  1. MatchQuote(tokenA, tokenB, isBuy, price, quoteAmount) - Try match an order based on how many quote tokens to spend;
  2. GetAmountOut(tokenFrom, tokenTo, startPrice, endPrice, amountIn) - Calculate like AMM GetAmountOut;
  3. GetAmountIn(tokenFrom, tokenTo, startPrice, endPrice, amountOut) - Calculate like AMM GetAmountIn.

SwapAggregator (Price Aggregator):

  1. GetAMMPrice(reverseBase, reverseQuote, quoteDecimals) - Calculate AMM price for comparison;
  2. GetAMMAmountInTillPrice(isBuy, price, quoteDecimals, reserveIn, reserveOut) - Calculate the amountIn to swap AMM to expected position;
  3. New GetAmountIn and GetAmountOut - Compare price and get all amountIn and amountOut for later swap;
  4. New GetAmountsIn and GetAmountsOut - Update;
  5. New SwapTokenOutForTokenIn and SwapTokenInForTokenOut - Update;
  6. SwapAMMTillPrice(sender, amountInMax, amountOutMin, tokenIn, tokenOut, isBuy, price, quoteDecimals, deadLine);
  7. SwapWithOrderBook(tokenIn, tokenOut, amountToBook, amountToPool, amountOutBook, amountOutPool, bookDealPrice) - Do swap for each step.

Note

  1. Price comparison has already taken different fees into consideration;
  2. RequestTransfer in master branch is used here as well;
  3. Gas fee has been somehow optimized, but still high.
txhsl commented 2 years ago

Adding Swap() liked methods... ... ...... ......... Now methods GetAmountIn() and GetAmountOut() are available.