From 3ed5a44f9b076af54b8290eed31f7b1bafecb940 Mon Sep 17 00:00:00 2001 From: 0xSamWitch Date: Mon, 29 Jan 2024 23:48:14 +0000 Subject: [PATCH] Fix rolling tick orders --- contracts/SamWitchOrderBook.sol | 11 ++- test/SamWitchOrderBook.ts | 115 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/contracts/SamWitchOrderBook.sol b/contracts/SamWitchOrderBook.sol index 917717c..cb3c316 100644 --- a/contracts/SamWitchOrderBook.sol +++ b/contracts/SamWitchOrderBook.sol @@ -15,6 +15,8 @@ import {BokkyPooBahsRedBlackTreeLibrary} from "./BokkyPooBahsRedBlackTreeLibrary import {IBrushToken} from "./interfaces/IBrushToken.sol"; import {ISamWitchOrderBook} from "./interfaces/ISamWitchOrderBook.sol"; +import "hardhat/console.sol"; + /// @title SamWitchOrderBook (SWOB) /// @author Sam Witch (PaintSwap & Estfor Kingdom) /// @author 0xDoubleSharp @@ -804,18 +806,21 @@ contract SamWitchOrderBook is ISamWitchOrderBook, ERC1155Holder, UUPSUpgradeable // Loop until we find a suitable place to put this while (true) { price_ = uint72(uint128(int72(price_) + _tickIncrement)); + console.log(price_); if (!_tree.exists(price_)) { _tree.insert(price_); break; } else if ( - (_packedOrdersPriceMap[price_].length.sub(tombstoneOffset)).mul(NUM_ORDERS_PER_SEGMENT) >= - maxOrdersPerPrice && + (_packedOrdersPriceMap[price_].length.sub(tombstoneOffset)).mul(NUM_ORDERS_PER_SEGMENT) < + maxOrdersPerPrice || uint( _packedOrdersPriceMap[price_][_packedOrdersPriceMap[price_].length.dec()] >> NUM_ORDERS_PER_SEGMENT.dec().mul(64) - ) != + ) == 0 ) { + // There are orders left or the last segment is not filled yet + console.log("WHAT"); break; } } diff --git a/test/SamWitchOrderBook.ts b/test/SamWitchOrderBook.ts index 821275b..25a8efe 100644 --- a/test/SamWitchOrderBook.ts +++ b/test/SamWitchOrderBook.ts @@ -1105,6 +1105,121 @@ describe("SamWitchOrderBook", function () { expect(orders.length).to.eq(1); }); + it("Max number of orders for a price should increment it by the tick, where the price level exists already and has spare segments", async function () { + const {orderBook, alice, tokenId, maxOrdersPerPrice, tick} = await loadFixture(deployContractsFixture); + + // Set up order book + const price = 100; + const quantity = 1; + + const limitOrder: ISamWitchOrderBook.LimitOrderStruct = { + side: OrderSide.Buy, + tokenId, + price, + quantity, + }; + + const limitOrders = new Array(maxOrdersPerPrice).fill(limitOrder); + await orderBook.limitOrders(limitOrders); + + await orderBook.connect(alice).limitOrders([ + { + side: OrderSide.Buy, + tokenId, + price: price - tick, + quantity, + }, + ]); + + // Try to add one more and it will be added to the next tick price + await orderBook.connect(alice).limitOrders([limitOrder]); + + let orders = await orderBook.allOrdersAtPrice(OrderSide.Buy, tokenId, price); + expect(orders.length).to.eq(maxOrdersPerPrice); + + orders = await orderBook.allOrdersAtPrice(OrderSide.Buy, tokenId, price - tick); + expect(orders.length).to.eq(2); + }); + + it("Multiple ticks iterated when there are the max number of orders and no spare orders in the last segment", async function () { + const {orderBook, alice, tokenId, maxOrdersPerPrice, erc1155, tick} = await loadFixture(deployContractsFixture); + + // Set up order book + const price = 100; + const quantity = 1; + await erc1155.mintSpecificId(tokenId, 10000); + + const limitOrder: ISamWitchOrderBook.LimitOrderStruct = { + side: OrderSide.Sell, + tokenId, + price, + quantity, + }; + + let limitOrders = new Array(maxOrdersPerPrice).fill(limitOrder); + await orderBook.limitOrders(limitOrders); + + const limitOrderNextTick: ISamWitchOrderBook.LimitOrderStruct = { + side: OrderSide.Sell, + tokenId, + price: price + tick, + quantity, + }; + + let limitOrdersNextTick = new Array(maxOrdersPerPrice).fill( + limitOrderNextTick, + ); + await orderBook.limitOrders(limitOrdersNextTick); + + // Try to add one more and it will be added to the tick * 2 price + await orderBook.connect(alice).limitOrders([limitOrder]); + + let orders = await orderBook.allOrdersAtPrice(OrderSide.Sell, tokenId, price + 2 * tick); + expect(orders.length).to.eq(1); + }); + + it("Multiple ticks iterated, space at the end of the segment for it", async function () { + const {orderBook, alice, tokenId, maxOrdersPerPrice, erc1155, tick} = await loadFixture(deployContractsFixture); + + // Set up order book + const price = 100; + const quantity = 1; + await erc1155.mintSpecificId(tokenId, 10000); + + const limitOrder: ISamWitchOrderBook.LimitOrderStruct = { + side: OrderSide.Sell, + tokenId, + price, + quantity, + }; + + let limitOrders = new Array(maxOrdersPerPrice).fill(limitOrder); + await orderBook.limitOrders(limitOrders); + + const limitOrderNextTick: ISamWitchOrderBook.LimitOrderStruct = { + side: OrderSide.Sell, + tokenId, + price: price + tick, + quantity, + }; + + let limitOrdersNextTick = new Array(maxOrdersPerPrice).fill( + limitOrderNextTick, + ); + await orderBook.limitOrders(limitOrdersNextTick); + + await orderBook.cancelOrders([maxOrdersPerPrice * 2 - 2], [{side: OrderSide.Sell, tokenId, price: price + tick}]); + + // Try to add one more and it will be added to the tick * 2 price + await orderBook.connect(alice).limitOrders([limitOrder]); + + let orders = await orderBook.allOrdersAtPrice(OrderSide.Sell, tokenId, price + tick); + expect(orders.length).to.eq(100); + + orders = await orderBook.allOrdersAtPrice(OrderSide.Sell, tokenId, price + 2 * tick); + expect(orders.length).to.eq(0); + }); + it("Price must be modulus of tick quantity must be > min quantity, sell", async function () { const {orderBook, erc1155, tokenId} = await loadFixture(deployContractsFixture);