Contract 0x033c00fd922af40b6683fe5371380831a5b81d57 2

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xec5565442925a4a0d154d98a7ab83fd8e2acfff955285b1ea1bb07f84f9c793f0x04010000753354542023-03-30 18:26:362 secs ago0x5ed5185a6845f810f61a552f325b3b3fbbc44089 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006972 0.1
0xc4f4ba08425552d94863266d247ddf0c7ca03f17e2561a8cc3e18320c36e16c20x01000100753354442023-03-30 18:26:344 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007616 0.1
0x78d1151ba865f0ae9f89b1837c533f5def2efd6a25414844ff8459075f66e1fa0x04000000753354362023-03-30 18:26:326 secs ago0x021e6aeb1ef6feb3dd31de167f81e382d9cd9aad IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007436 0.1
0x2036e48c783ef2284ccdae3889963d6babe106dffc3684931e7a36cd368653020x01010100753354132023-03-30 18:26:2612 secs ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007616 0.1
0xfc38bb870183d1e5d99ff677c3f78feb520f7a9feacd7be8d09395c5cf6d551f0x04010000753353732023-03-30 18:26:1622 secs ago0x3377126b8f8df9f5d875d5bb29a7b411694c906f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007377 0.1
0xaf90dd1055e167ed209f0c652fa4bc1ea41d50fd25eb055910c06638444082880x01000100753353732023-03-30 18:26:1622 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007616 0.1
0xc8591b8ed76adc8c32e6dce0738aae8c325937eeeca5b5fd81201238f47969af0x04000000753353532023-03-30 18:26:1127 secs ago0x08c3201362d910083a4dde0c765e38c35264c04f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006979 0.1
0xb66235b57fe8729d6757c40d21d4a32030636b2128e4e3b015d07bcec211b2c90x01010100753353412023-03-30 18:26:0830 secs ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007616 0.1
0xb3fc1fb9fe3ccae6c22843e21eae087c6433220ebb3f667c1984d9450bc813190x01000100753353382023-03-30 18:26:0731 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007578 0.1
0x6dcdb89094f76e41b5022998fcc1b591e17f49bc27ad475ac9a19aff2ca313d50x04010000753353292023-03-30 18:26:0533 secs ago0x5ed5185a6845f810f61a552f325b3b3fbbc44089 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007014 0.1
0x63cc4527350f66e5c738774c04dd9718c00f16615c8cb4bdeb79b6e172ce34c90x04000000753353092023-03-30 18:25:5939 secs ago0x0c0e075e40cd50d618616cea39344bc0a9de5daf IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007055 0.1
0x6dccd69357f235f7539b44346474f9a5b9fd370165641ac13499cef3574ddfcc0x01010100753353052023-03-30 18:25:5840 secs ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007668 0.1
0x55d7b0d8e5b0c07489433f381735540368cbc427d48c16ea7f7c135789a0f0ae0x01000100753353032023-03-30 18:25:5840 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007668 0.1
0x9a646393560c8476c9804b7c973cd7e7e864c63fb78f8b308cce3aa37a137b3a0x04000000753352972023-03-30 18:25:5642 secs ago0x3ab9d33899b782d3286d7f36a6b3f4445124f374 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006979 0.1
0x7bc66f292ff0e670b18615ce5cf30e4a9a85f4937cedefc91abb4d7282f4444a0x04010000753352792023-03-30 18:25:5246 secs ago0x3377126b8f8df9f5d875d5bb29a7b411694c906f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007014 0.1
0x3e81ba5e3b62fd6075de24a531d3bcdc3b903072433bf9bfc4ff10a53a0970ff0x01010100753352682023-03-30 18:25:4949 secs ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007616 0.1
0x98bbd52c944a08f32b21e8974c2ca536aa3d259104db152a884c36772ccf30400x01000200753352672023-03-30 18:25:4949 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00010054 0.1
0x88eeedb9a62cd47690481dfa4dd88fd2c36c88a734303a82c5f7f8af2add72da0x04000000753352592023-03-30 18:25:4751 secs ago0x4bc0cce77f312c76fed44e965a0f4a523efde8d7 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006776 0.1
0xfd085112c7cc2feba309bcb67f003e4da4e9bdcd2433afc15b92aa3861bb2f000x04010000753352352023-03-30 18:25:4157 secs ago0x5ed5185a6845f810f61a552f325b3b3fbbc44089 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007185 0.1
0x639d397cb49332c1ed42c27c26ff9473e44cb339e37d8b6db03fa79896ada3620x01000100753352322023-03-30 18:25:4058 secs ago0xe425f4dfe8b2446b686b2c5a7c17679b7170996e IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00008348 0.1
0x7209120bd5d5e7ab88dfe217703f311810e386f8876953e06b3a7995b9e973d60x01010100753352312023-03-30 18:25:4058 secs ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00007565 0.1
0xf9ea3528a9dff000ef16c98a308a23fd78df7cdd60210fa4e5e49ebc330852730x04010000753352222023-03-30 18:25:381 min ago0x3377126b8f8df9f5d875d5bb29a7b411694c906f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006972 0.1
0x9bff0f12b0eac4cdfc8c4794d00a6a02a45dabf19d8f2c5bd4c273bb0c08d10e0x04000000753352102023-03-30 18:25:351 min ago0x08c3201362d910083a4dde0c765e38c35264c04f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006824 0.1
0x1b7463f98a063e2c2167257a931565314446be06aa77a1714baab1f87ec8ae610x04010000753351962023-03-30 18:25:311 min ago0xa8d3725766d068308982d512a50ca9cbb2f6793f IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.00006881 0.1
0x0697778b03b86ef1684d30616474bcca383855326b6bf34985ebf839aaec34080x01010200753351942023-03-30 18:25:301 min ago0x8141b318a8ae0ae89fe1e919570bec662124e7b9 IN  0x033c00fd922af40b6683fe5371380831a5b81d570 ETH0.0001 0.1
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af40 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x7c30dadc489cc7c54254cb6453ee9a9886cc95c5 0x033c00fd922af40b6683fe5371380831a5b81d570 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d570x7c30dadc489cc7c54254cb6453ee9a9886cc95c50 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af40 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x7c30dadc489cc7c54254cb6453ee9a9886cc95c5 0x033c00fd922af40b6683fe5371380831a5b81d570 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d570x7c30dadc489cc7c54254cb6453ee9a9886cc95c50 ETH
0x2526fe3a37942df76cf2fd9006b7cd5ede6f51eea4c4e1260180c2ed9d300764720866622023-03-21 10:18:469 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af40 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af40 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x7c30dadc489cc7c54254cb6453ee9a9886cc95c5 0x033c00fd922af40b6683fe5371380831a5b81d570 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d570x7c30dadc489cc7c54254cb6453ee9a9886cc95c50 ETH
0xd18ceeae813e3fe808f2edbff8255b5debbaa02dd61b2b8a733adc06caaf15fe720866532023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af40 ETH
0xf031f757c0f04d9d4161612bfa1be5179cf82ee55eb2ae1163f2ddf2b1a6e742720866512023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x82af49447d8a07e3bd95bd0d56f35241523fbab10 ETH
0xf031f757c0f04d9d4161612bfa1be5179cf82ee55eb2ae1163f2ddf2b1a6e742720866512023-03-21 10:18:449 days 8 hrs ago 0x033c00fd922af40b6683fe5371380831a5b81d57 0x82af49447d8a07e3bd95bd0d56f35241523fbab10 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Router

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 12 : Router.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "./interfaces/IOrderBook.sol";
import "./interfaces/IBalanceChangeCallback.sol";
import "./interfaces/IFactory.sol";

import "./library/FullMath.sol";

/// @title Router
/// @notice Router for interacting with order books. The user can specify the
/// token pair or the orderBookId of the order book to interact with, and the
/// router will interact with the contract address for that order book
contract Router is IBalanceChangeCallback {
    using SafeERC20 for IERC20Metadata;
    IFactory public immutable factory;

    constructor(address factoryAddress) {
        factory = IFactory(factoryAddress);
    }

    /// @notice Returns the order book given the orderBookId of that order book.
    /// @param orderBookId The id of the order book to lookup
    /// @return orderBook The order book contract for orderBookId
    function getOrderBookFromId(
        uint8 orderBookId
    ) private view returns (IOrderBook) {
        address orderBookAddress = factory.getOrderBookFromId(orderBookId);
        require(orderBookAddress != address(0), "Invalid orderBookId");
        return IOrderBook(orderBookAddress);
    }

    /// @notice Create multiple limit orders in the order book
    /// @param orderBookId The unique identifier of the order book
    /// @param size The number of limit orders to create. The size of each
    /// argument array must be equal to this size
    /// @param amount0Base The amount of token0 for each limit order in terms
    /// of number of sizeTicks. The actual amount of token0 in order i will
    /// be amount0Base[i] * sizeTick
    /// @param priceBase The price of the token0 for each limit order
    /// in terms of token1 and size and price ticks. The actual amount of token1
    /// in the order will be priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param isAsk Whether each order is an ask order. isAsk = true means
    /// the order sells token0 for token1
    /// @param hintId Where to insert each order in the order book. Meant to
    /// be calculated off-chain using the getMockIndexToInsert function
    /// @return orderId The ids of each created order
    function createLimitOrderBatch(
        uint8 orderBookId,
        uint8 size,
        uint64[] memory amount0Base,
        uint64[] memory priceBase,
        bool[] memory isAsk,
        uint32[] memory hintId
    ) public returns (uint32[] memory orderId) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        orderId = new uint32[](size);
        for (uint8 i = 0; i < size; i++) {
            orderId[i] = orderBook.createLimitOrder(
                amount0Base[i],
                priceBase[i],
                isAsk[i],
                msg.sender,
                hintId[i]
            );
        }
    }

    /// @notice Create limit order in the order book
    /// @param orderBookId The unique identifier of the order book
    /// @param amount0Base The amount of token0 in terms of number of sizeTicks.
    /// The actual amount of token0 in the order will be newAmount0Base * sizeTick
    /// @param priceBase The price of the token0 in terms of token1 and size
    /// and price ticks. The actual amount of token1 in the order will be
    /// priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param isAsk isAsk = true means the order sells token0 for token1
    /// @param hintId Where to insert order in the order book. Meant to
    /// be calculated off-chain using the getMockIndexToInsert function
    /// @return orderId The id of the created order
    function createLimitOrder(
        uint8 orderBookId,
        uint64 amount0Base,
        uint64 priceBase,
        bool isAsk,
        uint32 hintId
    ) public returns (uint32 orderId) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        orderId = orderBook.createLimitOrder(
            amount0Base,
            priceBase,
            isAsk,
            msg.sender,
            hintId
        );
    }

    /// @notice Cancels and creates multiple limit orders in the order book
    /// @param orderBookId The unique identifier of the order book
    /// @param size The number of limit orders to cancel and create. The size of each
    /// argument array must be equal to this size
    /// @param orderId The ids of the orders to update
    /// @param newAmount0Base The amount of token0 for each updated limit order
    /// in terms of number of sizeTicks. The actual amount of token0 in the
    /// order will be newAmount0Base * sizeTick
    /// @param newPriceBase The price of the token0 for each limit order
    /// in terms of token1 and size and price ticks. The actual amount of token1
    /// in the order will be priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param hintId Where to insert each new order in the order book. Meant to
    /// be calculated off-chain using the getMockIndexToInsert function
    /// @return newOrderId The new ids of the each updated order
    function updateLimitOrderBatch(
        uint8 orderBookId,
        uint8 size,
        uint32[] memory orderId,
        uint64[] memory newAmount0Base,
        uint64[] memory newPriceBase,
        uint32[] memory hintId
    ) public returns (uint32[] memory newOrderId) {
        newOrderId = new uint32[](size);
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        bool isCanceled;
        bool isAsk;
        for (uint256 i = 0; i < size; i++) {
            if (!orderBook.isOrderActive(orderId[i])) {
                newOrderId[i] = 0;
                continue;
            }
            isAsk = orderBook.isAskOrder(orderId[i]);
            isCanceled = orderBook.cancelLimitOrder(orderId[i], msg.sender);

            // Shouldn't happen since function checks if the order is active above
            require(isCanceled, "Could not cancel the order");

            newOrderId[i] = orderBook.createLimitOrder(
                newAmount0Base[i],
                newPriceBase[i],
                isAsk,
                msg.sender,
                hintId[i]
            );
        }
    }

    /// @notice Cancel limit order in the order book and create a new one
    /// @param orderBookId The unique identifier of the order book
    /// @param orderId The id of the order to cancel
    /// @param newAmount0Base The amount of token0 in terms of number of sizeTicks.
    /// The actual amount of token0 in the order will be newAmount0Base * sizeTick
    /// @param newPriceBase The price of the token0 in terms of token1 and size
    /// and price ticks. The actual amount of token1 in the order will be
    /// priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param hintId Where to insert new order in the order book. Meant to
    /// be calculated off-chain using the getMockIndexToInsert function
    /// @return newOrderId The new id of the updated order
    function updateLimitOrder(
        uint8 orderBookId,
        uint32 orderId,
        uint64 newAmount0Base,
        uint64 newPriceBase,
        uint32 hintId
    ) public returns (uint32 newOrderId) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);

        if (!orderBook.isOrderActive(orderId)) {
            newOrderId = 0;
        } else {
            bool isAsk = orderBook.isAskOrder(orderId);
            bool isCanceled = orderBook.cancelLimitOrder(orderId, msg.sender);

            // Shouldn't happen since function checks if the order is active above
            require(isCanceled, "Could not cancel the order");
            newOrderId = orderBook.createLimitOrder(
                newAmount0Base,
                newPriceBase,
                isAsk,
                msg.sender,
                hintId
            );
        }
    }

    /// @notice Cancel multiple limit orders in the order book
    /// @dev Including an inactive order in the batch cancelation does not
    /// revert. This is to make it easier for market markers to cancel
    /// @param orderBookId The unique identifier of the order book
    /// @param size The number of limit orders to update. The size of each
    /// argument array must be equal to this size
    /// @param orderId The ids of the orders to cancel
    /// @return isCanceled List of booleans indicating whether each order was successfully
    /// canceled
    function cancelLimitOrderBatch(
        uint8 orderBookId,
        uint8 size,
        uint32[] memory orderId
    ) public returns (bool[] memory isCanceled) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        isCanceled = new bool[](size);
        for (uint256 i = 0; i < size; i++) {
            isCanceled[i] = orderBook.cancelLimitOrder(orderId[i], msg.sender);
        }
    }

    /// @notice Cancel single limit order in the order book
    /// @param orderBookId The unique identifier of the order book
    /// @param orderId The id of the orders to cancel
    /// @return isCanceled A boolean indicating whether the order was successfully canceled
    function cancelLimitOrder(
        uint8 orderBookId,
        uint32 orderId
    ) public returns (bool) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.cancelLimitOrder(orderId, msg.sender);
    }

    /// @notice Create a market order in the order book
    /// @param orderBookId The unique identifier of the order book
    /// @param amount0Base The amount of token0 in the limit order in terms
    /// of number of sizeTicks. The actual amount of token0 in the order will
    /// be amount0Base * sizeTick
    /// @param priceBase The price of the token0 in terms of token1 and size
    /// and price ticks. The actual amount of token1 in the order will be
    /// priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param isAsk Whether the order is an ask order. isAsk = true means
    /// the order sells token0 for token1
    function createMarketOrder(
        uint8 orderBookId,
        uint64 amount0Base,
        uint64 priceBase,
        bool isAsk
    ) public {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        orderBook.createMarketOrder(amount0Base, priceBase, isAsk, msg.sender);
    }

    /// @notice Claims base tokens from order book
    /// @param orderBookId The unique identifier of the order book
    function claimBaseToken(uint8 orderBookId) public {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        orderBook.claimBaseToken(msg.sender);
    }

    /// @notice Claims quote tokens from order book
    /// @param orderBookId The unique identifier of the order book
    function claimQuoteToken(uint8 orderBookId) public {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        orderBook.claimQuoteToken(msg.sender);
    }


    /// @inheritdoc IBalanceChangeCallback
    function subtractSafeBalanceCallback(
        IERC20Metadata tokenToTransferFrom,
        address from,
        uint256 amount,
        uint8 orderBookId
    ) external override {
        require(
            msg.sender == address(getOrderBookFromId(orderBookId)),
            "Caller does not match order book"
        );
        uint256 balance = tokenToTransferFrom.balanceOf(from);
        require(
            amount <= balance,
            "Insufficient funds associated with sender's address"
        );
        uint256 orderBookBalanceBefore = tokenToTransferFrom.balanceOf(msg.sender);
        tokenToTransferFrom.safeTransferFrom(from, msg.sender, amount);
        uint256 orderBookBalanceAfter = tokenToTransferFrom.balanceOf(msg.sender);
        require(
            orderBookBalanceAfter >= orderBookBalanceBefore + amount,
            "Order book balance change does not match the received amount"
        );
    }

    /// @notice Get the order details of all limit orders in the order book.
    /// Each returned list contains the details of ask orders first, followed
    /// by bid orders
    /// @param orderBookId The id of the order book to lookup
    /// @return id The ids of the orders
    /// @return owner The addresses of the orders' owners
    /// @return amount0 The amount of token0 remaining in the orders
    /// @return amount1 The amount of token1 remaining in the orders
    /// @return isAsk Whether each order is an ask order
    function getLimitOrders(uint8 orderBookId)
        external
        view
        returns (
            uint32[] memory,
            address[] memory,
            uint256[] memory,
            uint256[] memory,
            bool[] memory
        )
    {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.getLimitOrders();
    }

    /// @notice Get the order details of the ask order with the lowest price
    /// in the order book
    /// @param orderBookId The id of the order book to lookup
    /// @return bestAsk LimitOrder data struct of the best ask order
    function getBestAsk(
        uint8 orderBookId
    ) external view returns (LimitOrder memory) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.getBestAsk();
    }

    /// @notice Get the order details of the bid order with the highest price
    /// in the order book
    /// @param orderBookId The id of the order book to lookup
    /// @return bestBid LimitOrder data struct of the best bid order
    function getBestBid(
        uint8 orderBookId
    ) external view returns (LimitOrder memory) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.getBestBid();
    }

    /// @notice Find the order id to the left of where the new order
    /// should be inserted. Meant to be used off-chain to find the
    /// hintId for the createLimitOrder and updateLimitOrder functions
    /// @param orderBookId The id of the order book to lookup
    /// @param amount0 The amount of token0 in the new order
    /// @param amount1 The amount of token1 in the new order
    /// @param isAsk Whether the new order is an ask order
    /// @return hintId The id of the order to the left of where the new order
    /// should be inserted
    function getMockIndexToInsert(
        uint8 orderBookId,
        uint256 amount0,
        uint256 amount1,
        bool isAsk
    ) external view returns (uint32) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.getMockIndexToInsert(amount0, amount1, isAsk);
    }

    /// @notice Get the amount of base token claimable by the owner for given order book
    /// @param orderBookId The id of the order book to lookup
    /// @param owner The address of the user
    /// @return claimableBaseToken Claimable base token amount for given order book and address
    function claimableBaseToken(
        uint8 orderBookId,
        address owner
    ) external view returns (uint256) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.claimableBaseToken(owner);
    }

    /// @notice Get the amount of quote token claimable by the owner for given order book
    /// @param orderBookId The id of the order book to lookup
    /// @param owner The address of the user
    /// @return claimableQuoteToken Claimable quote token amount for given order book and address
    function claimableQuoteToken(
        uint8 orderBookId,
        address owner
    ) external view returns (uint256) {
        IOrderBook orderBook = getOrderBookFromId(orderBookId);
        return orderBook.claimableQuoteToken(owner);
    }

    /// @dev Get the uint value from msg.data starting from a specific byte
    /// @param startByte The starting byte
    /// @param length The number of bytes to read
    /// @return val Parsed uint256 value from calldata
    function parseCallData(
        uint256 startByte,
        uint256 length
    ) private pure returns (uint256) {
        uint256 val;

        require(length <= 32, "Length limit is 32 bytes");

        require(
            length + startByte <= msg.data.length,
            "trying to read past end of calldata"
        );

        assembly {
            val := calldataload(startByte)
        }

        val = val >> (256 - length * 8);

        return val;
    }

    /// @notice This function is called when no other router function is
    /// called. The data should be passed in msg.data.
    /// The first byte of msg.data should be the function selector
    /// 1 = createLimitOrder
    /// 2 = updateLimitOrder
    /// 3 = cancelLimitOrder
    /// 4 = createMarketOrder
    /// The next byte should be the orderBookId of the order book
    /// The next byte should be the number of orders to batch. This is ignored
    /// for the createMarketOrder function
    /// Then, for data for each order is read in a loop
    fallback() external {
        uint256 _func;

        _func = parseCallData(0, 1);
        uint8 orderBookId = uint8(parseCallData(1, 1));
        uint8 batchSize = uint8(parseCallData(2, 1));
        uint256 currentByte = 3;
        uint64[] memory amount0Base = new uint64[](batchSize);
        uint64[] memory priceBase = new uint64[](batchSize);
        uint32[] memory hintId = new uint32[](batchSize);
        uint32[] memory orderId = new uint32[](batchSize);

        // createLimitOrder
        if (_func == 1) {
            bool[] memory isAsk = new bool[](batchSize);
            uint8 isAskByte;
            for (uint256 i = 0; i < batchSize; i++) {
                amount0Base[i] = uint64(parseCallData(currentByte, 8));
                priceBase[i] = uint64(parseCallData(currentByte + 8, 8));
                isAskByte = uint8(parseCallData(currentByte + 16, 1));
                require(isAskByte <= 1, "Invalid isAsk");
                isAsk[i] = isAskByte == 1;
                hintId[i] = uint32(parseCallData(currentByte + 17, 4));
                currentByte += 21;
            }
            createLimitOrderBatch(
                orderBookId,
                batchSize,
                amount0Base,
                priceBase,
                isAsk,
                hintId
            );
        }

        // updateLimitOrder
        if (_func == 2) {
            for (uint256 i = 0; i < batchSize; i++) {
                orderId[i] = uint32(parseCallData(currentByte, 4));
                amount0Base[i] = uint64(parseCallData(currentByte + 4, 8));
                priceBase[i] = uint64(parseCallData(currentByte + 12, 8));
                hintId[i] = uint32(parseCallData(currentByte + 20, 4));
                currentByte += 24;
            }
            updateLimitOrderBatch(
                orderBookId,
                batchSize,
                orderId,
                amount0Base,
                priceBase,
                hintId
            );
        }

        // cancelLimitOrder
        if (_func == 3) {
            for (uint256 i = 0; i < batchSize; i++) {
                orderId[i] = uint32(parseCallData(currentByte, 4));
                currentByte += 4;
            }
            cancelLimitOrderBatch(orderBookId, batchSize, orderId);
        }

        // createMarketOrder
        if (_func == 4) {
            uint8 isAskByte = uint8(parseCallData(18, 1));
            require(isAskByte <= 1, "Invalid isAsk");
            createMarketOrder(
                orderBookId,
                uint64(parseCallData(2, 8)),
                uint64(parseCallData(10, 8)),
                isAskByte == 1
            );
        }
    }
}

File 2 of 12 : IBalanceChangeCallback.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @title OrderFillCallback interface
/// @notice Callback for updating token balances
interface IBalanceChangeCallback {
    /// @notice Transfer tokens from the user to the contract
    /// @param tokenToTransferFrom The token to transfer from
    /// @param from The user to transfer from
    /// @param amount The amount to transfer
    /// @param orderBookId Id of caller the order book
    function subtractSafeBalanceCallback(
        IERC20Metadata tokenToTransferFrom,
        address from,
        uint256 amount,
        uint8 orderBookId
    ) external;
}

File 3 of 12 : IOrderBook.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import "../library/LinkedList.sol";

/// @title Order Book Interface
/// @notice An order book facilitates placing limit and market orders to trade
/// two assets which conform to the ERC20 specification. token0 is the asset
/// traded in the order book, and token1 is the asset paid/received for trading
/// token0
interface IOrderBook {
    /// @notice Create a limit order in the order book. The order will be
    /// filled by existing orders if there is a price overlap. If the order
    /// is not fully filled, it will be added to the order book
    /// @param amount0Base The amount of token0 in the limit order in terms
    /// of number of sizeTicks. The actual amount of token0 in the order will
    /// be amount0Base * sizeTick.
    /// @param priceBase The price of the token0 in terms of token1 and size
    /// and price ticks. The actual amount of token1 in the order will be
    /// priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param isAsk Whether the order is an ask order. isAsk = true means
    /// the order sells token0 for token1
    /// @param from The address of the order sender
    /// @param hintId Where to insert the order in the order book. Meant to
    /// be calculated off-chain using the getMockIndexToInsert function
    /// @return id The id of the order
    function createLimitOrder(
        uint64 amount0Base,
        uint64 priceBase,
        bool isAsk,
        address from,
        uint32 hintId
    ) external returns (uint32);

    /// @notice Cancel an existing limit order in the order book. Refunds the
    /// remaining tokens in the order to the owner
    /// @param id The id of the order to cancel
    /// @param from The address of the order sender
    /// @return isCanceled Whether the order was successfully canceled
    function cancelLimitOrder(uint32 id, address from) external returns (bool);

    /// @notice Create a market order in the order book. The order will be
    /// filled by existing orders if there is a price overlap. If the order
    /// is not fully filled, it will NOT be added to the order book
    /// @param amount0Base The amount of token0 in the limit order in terms
    /// of number of sizeTicks. The actual amount of token0 in the order will
    /// be amount0Base * sizeTick
    /// @param priceBase The price of the token0 in terms of token1 and size
    /// and price ticks. The actual amount of token1 in the order will be
    /// priceBase * amount0Base * priceTick * sizeTick / dec0
    /// @param isAsk Whether the order is an ask order. isAsk = true means
    /// the order sells token0 for token1
    /// @param from The address of the order sender
    function createMarketOrder(
        uint64 amount0Base,
        uint64 priceBase,
        bool isAsk,
        address from
    ) external;

    /// @notice Sends the matched base token from the order book to the owner. Only needs
    /// to be used if owners maker order gets matched but fails to receive the tokens
    /// @param owner The address of the owner of the claimable base token
    function claimBaseToken(address owner) external;

    /// @notice Sends the matched quote token from the order book to the owner. Only needs
    /// to be used if owners maker order gets matched but fails to receive the tokens
    /// @param owner The address of the owner of the claimable quote token
    function claimQuoteToken(address owner) external;

    /// @notice Get the order details of all limit orders in the order book.
    /// Each returned list contains the details of ask orders first, followed
    /// by bid orders
    /// @return id The ids of the orders
    /// @return owner The addresses of the orders' owners
    /// @return amount0 The amount of token0 remaining in the orders
    /// @return amount1 The amount of token1 remaining in the orders
    /// @return isAsk Whether each order is an ask order
    function getLimitOrders()
        external
        view
        returns (
            uint32[] memory,
            address[] memory,
            uint256[] memory,
            uint256[] memory,
            bool[] memory
        );

    /// @notice Get the order details of the ask order with the lowest price
    /// in the order book
    /// @return bestAsk LimitOrder data struct of the best ask order
    function getBestAsk() external view returns (LimitOrder memory);

    /// @notice Get the order details of the bid order with the highest price
    /// in the order book
    /// @return bestBid LimitOrder data struct of the best bid order
    function getBestBid() external view returns (LimitOrder memory);

    /// @notice Return whether an order is active
    /// @param id The id of the order
    /// @return isActive True if the order is active, false otherwise
    function isOrderActive(uint32 id) external view returns (bool);

    /// @notice Return whether an order is an ask order or not, fails if order is not active
    /// @param id The id of the order
    /// @return isActive True if the order is an ask order, false otherwise
    function isAskOrder(uint32 id) external view returns (bool);

    /// @notice Find the order id to the left of where the new order
    /// should be inserted. Meant to be used off-chain to find the
    /// hintId for the createLimitOrder functions
    /// @param amount0 The amount of token0 in the new order
    /// @param amount1 The amount of token1 in the new order
    /// @param isAsk Whether the new order is an ask order
    /// @return hintId The id of the order to the left of where the new order
    /// should be inserted
    function getMockIndexToInsert(
        uint256 amount0,
        uint256 amount1,
        bool isAsk
    ) external view returns (uint32);

    /// @notice Id of the order book
    /// @return orderBookId The unique identifier of an order book
    function orderBookId() external view returns (uint8);

    /// @notice The base token
    /// @return token0 The base token contract
    function token0() external view returns (IERC20Metadata);

    /// @notice The quote token
    /// @return token1 The quote token contract
    function token1() external view returns (IERC20Metadata);

    /// @notice The sizeTick of the order book
    /// @return sizeTick The sizeTick of the order book
    function sizeTick() external view returns (uint128);

    /// @notice The priceTick of the order book
    /// @return priceTick The priceTick of the order book
    function priceTick() external view returns (uint128);

    /// @notice The priceMultiplier of the order book
    /// @return priceMultiplier The priceMultiplier of the order book
    function priceMultiplier() external view returns (uint128);

    /// @notice Claimable base token amount for given address
    /// @return claimableBaseToken Claimable base token amount for given address
    function claimableBaseToken(address owner) external view returns (uint256);

    /// @notice Claimable quote token amount for given address
    /// @return claimableBaseToken Claimable quote token amount for given address
    function claimableQuoteToken(address owner) external view returns (uint256);
}

File 4 of 12 : FullMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "@openzeppelin/contracts/utils/math/Math.sol";

library FullMath {
    /// @notice Returns a*b/denominator, throws if remainder is not 0
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        require(denominator != 0, "Can not divide with 0");
        uint256 remainder = 0;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        require(remainder == 0, "Divison has a positive remainder");
        return Math.mulDiv(a, b, denominator);
    }

    /// @notice Returns true if a*b < c*d
    function mulCompare(
        uint256 a,
        uint256 b,
        uint256 c,
        uint256 d
    ) internal pure returns (bool result) {
        uint256 prod0; // Least significant 256 bits of the product a*b
        uint256 prod1; // Most significant 256 bits of the product a*b
        assembly {
            let mm := mulmod(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        uint256 prod2; // Least significant 256 bits of the product c*d
        uint256 prod3; // Most significant 256 bits of the product c*d
        assembly {
            let mm := mulmod(c, d, not(0))
            prod2 := mul(c, d)
            prod3 := sub(sub(mm, prod2), lt(mm, prod2))
        }

        if (prod1 < prod3) return true;
        if (prod3 < prod1) return false;
        if (prod0 < prod2) return true;
        return false;
    }
}

File 5 of 12 : IFactory.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

/// @title Factory Interface
/// @notice The Factory facilitates creation of order books
interface IFactory {
    /// @notice Event emitted when an order book is created
    /// @param orderBookId The id of the order book
    /// @param orderBookAddress The address of the created orderBook
    /// @param token0 The base token of the orderBook
    /// @param token1 The quote token of the orderBook
    /// @param logSizeTick Log10 of base token tick
    /// amount0 % 10**logSizeTick = 0 should be satisfied
    /// @param logPriceTick Log10 of price tick amount1 * dec0 % amount = 0
    /// and amount1 * dec0 / amount0 % 10**logPriceTick = 0 should be satisfied
    event OrderBookCreated(
        uint8 orderBookId,
        address orderBookAddress,
        address token0,
        address token1,
        uint8 logSizeTick,
        uint8 logPriceTick
    );

    /// @notice Event emitted when the owner is changed
    /// @param owner Address of the new owner
    event OwnerChanged(address owner);

    /// @notice Returns the current owner of the factory
    /// @return owner The address of the factory owner
    function owner() external view returns (address);

    /// @notice Set the router address for the factory. The router address
    /// can only be set once
    /// @param routerAddress The address of the router
    function setRouter(address routerAddress) external;

    /// @notice Set the owner of the factory
    /// @param _owner The address of the new owner
    function setOwner(address _owner) external;

    /// @notice Returns the address of the order book for a given token pair,
    /// or address 0 if it does not exist
    /// @dev token0 and token1 may be passed in either order
    /// @param token0 The contract address the first token
    /// @param token1 The contract address the second token
    /// @return orderBookAddress The address of the order book
    function getOrderBookFromTokenPair(address token0, address token1)
        external
        view
        returns (address);

    /// @notice Returns the address of the order book for the given order book id
    /// @param orderBookId The id of the order book to lookup
    /// @return orderBookAddress The address of the order book
    function getOrderBookFromId(uint8 orderBookId)
        external
        view
        returns (address);

    /// @notice Returns the details of the order book for a given token pair
    /// @param token0 The first token of the order book
    /// @param token1 The second token of the order book
    /// @return orderBookId The id of the order book
    /// @return orderBookAddress The address of the order book
    /// @return token0 The base token of the order book
    /// @return token1 The quote token of the order book
    /// @return sizeTick The size tick of the order book
    /// @return priceTick The price tick of the order book
    function getOrderBookDetailsFromTokenPair(address token0, address token1)
        external
        view
        returns (
            uint8,
            address,
            address,
            address,
            uint128,
            uint128
        );

    /// @notice Returns the details of the order book for a given order book id
    /// @param orderBookId The id of the order book to lookup
    /// @return orderBookId The id of the order book
    /// @return orderBookAddress The address of the order book
    /// @return token0 The base token of the order book
    /// @return token1 The quote token of the order book
    /// @return sizeTick The size tick of the order book
    /// @return priceTick The price tick of the order book
    function getOrderBookDetailsFromId(uint8 orderBookId)
        external
        view
        returns (
            uint8,
            address,
            address,
            address,
            uint128,
            uint128
        );

    /// @notice Creates a orderBook for the given two tokens
    /// @dev token0 and token1 may be passed in either order
    /// @param token0 The contract address the first token
    /// @param token1 The contract address the second token
    /// @param logSizeTick Log10 of base token tick
    /// amount0 % 10**logSizeTick = 0 should be satisfied
    /// @param logPriceTick Log10 of price tick amount1 * dec0 % amount = 0
    /// and amount1 * dec0 / amount0 % 10**logPriceTick = 0 should be satisfied
    /// @return orderBookAddress The address of the newly created orderBook
    function createOrderBook(
        address token0,
        address token1,
        uint8 logSizeTick,
        uint8 logPriceTick
    ) external returns (address);
}

File 6 of 12 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 7 of 12 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 8 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 9 of 12 : LinkedList.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "./FullMath.sol";

/// @notice Struct containing limit order data
struct LimitOrder {
    uint32 id;
    address owner;
    uint256 amount0;
    uint256 amount1;
}

/// @notice Struct for linked list node
/// @dev Each order id is mapped to a Node in the linked list
struct Node {
    uint32 prev;
    uint32 next;
    bool active;
}

/// @notice Struct for linked list sorted by price in non-decreasing order.
/// Used to store ask limit orders in the order book.
/// @dev Each order id is mapped to a Node and a LimitOrder
struct MinLinkedList {
    mapping(uint32 => Node) list;
    mapping(uint32 => LimitOrder) idToLimitOrder;
}

/// @notice Struct for linked list sorted in non-increasing order
/// Used to store bid limit orders in the order book.
/// @dev Each order id is mapped to a Node and a Limit Order
struct MaxLinkedList {
    mapping(uint32 => Node) list;
    mapping(uint32 => LimitOrder) idToLimitOrder;
}

/// @title MinLinkedListLib
/// @notice Library for linked list sorted in non-decreasing order
/// @dev Order ids 0 and 1 are special values. The first node of the
/// linked list has order id 0 and the last node has order id 1.
/// Order 0 should be initalized (in OrderBook.sol) with the lowest
/// possible price, and order 1 should be initialized with the highest
library MinLinkedListLib {
    /// @notice Comparison function for linked list. Returns true
    /// if the price of order id0 is strictly less than the price of order id1
    function compare(
        MinLinkedList storage listData,
        uint32 id0,
        uint32 id1
    ) internal view returns (bool) {
        return
            FullMath.mulCompare(
                listData.idToLimitOrder[id0].amount1,
                listData.idToLimitOrder[id1].amount0,
                listData.idToLimitOrder[id1].amount1,
                listData.idToLimitOrder[id0].amount0
            );
    }

    /// @notice Find the order id to the left of where the new order
    /// should be inserted
    /// @param orderId The order id to insert
    /// @param hintId The order id to start searching from
    function findIndexToInsert(
        MinLinkedList storage listData,
        uint32 orderId,
        uint32 hintId
    ) internal view returns (uint32) {
        // No element in the linked list can have next = 0, it means hintId is not in the linked list
        require(listData.list[hintId].next != 0, "Invalid hint id");

        while (!listData.list[hintId].active) {
            hintId = listData.list[hintId].next;
        }

        // After the two while loops, hintId will be the order id to the
        // left of where the new order should be inserted.
        while (hintId != 1) {
            uint32 nextId = listData.list[hintId].next;
            if (compare(listData, orderId, nextId)) break;
            hintId = nextId;
        }

        while (hintId != 0) {
            uint32 prevId = listData.list[hintId].prev;
            if (!compare(listData, orderId, hintId)) break;
            hintId = prevId;
        }

        return hintId;
    }

    /// @notice Inserts an order id into the linked list in sorted order
    /// @param orderId The order id to insert
    /// @param hintId The order id to begin searching for the position to
    /// insert the new order. Can be 0, 1, or the id of an actual order
    function insert(
        MinLinkedList storage listData,
        uint32 orderId,
        uint32 hintId
    ) public {
        uint32 indexToInsert = findIndexToInsert(listData, orderId, hintId);

        uint32 next = listData.list[indexToInsert].next;
        listData.list[orderId] = Node({
            prev: indexToInsert,
            next: next,
            active: true
        });
        listData.list[indexToInsert].next = orderId;
        listData.list[next].prev = orderId;
    }

    /// @notice Remove an order id from the linked list
    /// @dev Updates the linked list but does not delete the order id from
    /// the idToLimitOrder mapping
    /// @param orderId The order id to remove
    function erase(MinLinkedList storage listData, uint32 orderId) public {
        require(orderId > 1, "Cannot erase dummy orders");
        require(
            listData.list[orderId].active,
            "Cannot cancel an already inactive order"
        );

        uint32 prev = listData.list[orderId].prev;
        uint32 next = listData.list[orderId].next;

        listData.list[prev].next = next;
        listData.list[next].prev = prev;
        listData.list[orderId].active = false;
    }

    /// @notice Get the first order id in the linked list. Since the linked
    /// list is sorted, this gets the order id with the lowest price, if all
    /// the orders are dummy orders, returns 1
    /// @dev Order id 0 is a dummy value and should not be returned
    function getFirstNode(MinLinkedList storage listData)
        internal
        view
        returns (uint32)
    {
        return listData.list[0].next;
    }

    /// @notice Get the LimitOrder data struct for the first order
    function getTopLimitOrder(MinLinkedList storage listData)
        public
        view
        returns (LimitOrder storage)
    {
        require(!isEmpty(listData), "Book side is empty");
        return listData.idToLimitOrder[getFirstNode(listData)];
    }

    /// @notice Returns true if the linked list has no orders
    /// @dev Order id 0 and 1 are dummy values, so the linked list
    /// is empty if those are the only two orders
    function isEmpty(MinLinkedList storage listData)
        public
        view
        returns (bool)
    {
        return getFirstNode(listData) == 1;
    }

    /// @notice Returns the number of orders in the linked list
    /// @dev Order id 0 and 1 are dummy values, so the number of
    /// orders does not include them
    function size(MinLinkedList storage listData) public view returns (uint32) {
        uint32 listSize = 0;
        for (
            uint32 pointer = getFirstNode(listData);
            pointer != 1;
            pointer = listData.list[pointer].next
        ) ++listSize;
        return listSize;
    }

    /// @notice Returns a list of LimitOrder data structs for each
    /// order in the linked list
    /// @dev Order id 0 and 1 are dummy values, so the returned list
    /// does not include them
    function getOrders(MinLinkedList storage listData)
        public
        view
        returns (LimitOrder[] memory orders)
    {
        orders = new LimitOrder[](size(listData));
        uint32 i = 0;
        for (
            uint32 pointer = getFirstNode(listData);
            pointer != 1;
            pointer = listData.list[pointer].next
        ) {
            orders[i] = listData.idToLimitOrder[pointer];
            ++i;
        }
    }

    /// @notice Comparison function for linked list. Returns true if the
    /// price of amount0 to amount1 is less than the price of order id1
    function mockCompare(
        MinLinkedList storage listData,
        uint256 amount0,
        uint256 amount1,
        uint32 id1
    ) internal view returns (bool) {
        return
            FullMath.mulCompare(
                amount1,
                listData.idToLimitOrder[id1].amount0,
                listData.idToLimitOrder[id1].amount1,
                amount0
            );
    }

    /// @notice Find the order id to the left of where the new order
    /// should be inserted. Meant to be used off-chain to find the
    /// hintId for the insert function
    /// @param amount0 The amount of token0 in the new order
    /// @param amount1 The amount of token1 in the new order
    function getMockIndexToInsert(
        MinLinkedList storage listData,
        uint256 amount0,
        uint256 amount1
    ) public view returns (uint32) {
        uint32 hintId = 0;

        // After the two while loops, hintId will be the order id to the
        // left of where the new order should be inserted.
        while (hintId != 1) {
            uint32 nextId = listData.list[hintId].next;
            if (mockCompare(listData, amount0, amount1, nextId)) break;
            hintId = nextId;
        }

        while (hintId != 0) {
            uint32 prevId = listData.list[hintId].prev;
            if (!mockCompare(listData, amount0, amount1, hintId)) break;
            hintId = prevId;
        }

        return hintId;
    }
}

/// @title MaxLinkedListLib
/// @notice Library for linked list sorted in non-increasing order
/// @dev Order ids 0 and 1 are special values. The first node of the
/// linked list has order id 0 and the last node has order id 1.
/// Order 0 should be initalized (in OrderBook.sol) with the highest
/// possible price, and order 1 should be initialized with the lowest
library MaxLinkedListLib {
    /// @notice Comparison function for linked list. Returns true
    /// if the price of order id0 is strictly greater than the price of order id1
    function compare(
        MaxLinkedList storage listData,
        uint32 id0,
        uint32 id1
    ) internal view returns (bool) {
        return
            FullMath.mulCompare(
                listData.idToLimitOrder[id1].amount1,
                listData.idToLimitOrder[id0].amount0,
                listData.idToLimitOrder[id1].amount0,
                listData.idToLimitOrder[id0].amount1
            );
    }

    /// @notice Find the order id to the left of where the new order
    /// should be inserted
    /// @param orderId The order id to insert
    /// @param hintId The order id to start searching from
    function findIndexToInsert(
        MaxLinkedList storage listData,
        uint32 orderId,
        uint32 hintId
    ) internal view returns (uint32) {
        // No element in the linked list can have next = 0, it means hintId is not in the linked list
        require(listData.list[hintId].next != 0, "Invalid hint id");

        while (!listData.list[hintId].active) {
            hintId = listData.list[hintId].next;
        }

        // After the two while loops, hintId will be the order id to the
        // left of where the new order should be inserted.
        while (hintId != 1) {
            uint32 nextId = listData.list[hintId].next;
            if (compare(listData, orderId, nextId)) break;
            hintId = nextId;
        }

        while (hintId != 0) {
            uint32 prevId = listData.list[hintId].prev;
            if (!compare(listData, orderId, hintId)) break;
            hintId = prevId;
        }

        return hintId;
    }

    /// @notice Inserts an order id into the linked list in sorted order
    /// @param orderId The order id to insert
    /// @param hintId The order id to begin searching for the position to
    /// insert the new order. Can be 0, 1, or the id of an actual order
    function insert(
        MaxLinkedList storage listData,
        uint32 orderId,
        uint32 hintId
    ) public {
        uint32 indexToInsert = findIndexToInsert(listData, orderId, hintId);

        uint32 next = listData.list[indexToInsert].next;
        listData.list[orderId] = Node({
            prev: indexToInsert,
            next: next,
            active: true
        });
        listData.list[indexToInsert].next = orderId;
        listData.list[next].prev = orderId;
    }

    /// @notice Remove an order id from the linked list
    /// @dev Updates the linked list but does not delete the order id from
    /// the idToLimitOrder mapping
    /// @param orderId The order id to remove
    function erase(MaxLinkedList storage listData, uint32 orderId) public {
        require(orderId > 1, "Cannot erase dummy orders");
        require(
            listData.list[orderId].active,
            "Cannot cancel an already inactive order"
        );

        uint32 prev = listData.list[orderId].prev;
        uint32 next = listData.list[orderId].next;

        listData.list[prev].next = next;
        listData.list[next].prev = prev;
        listData.list[orderId].active = false;
    }

    /// @notice Get the first order id in the linked list. Since the linked
    /// list is sorted, this gets the order id with the highest price, if all
    /// the orders are dummy orders, returns 1
    /// @dev Order id 0 is a dummy value and should not be returned
    function getFirstNode(MaxLinkedList storage listData)
        internal
        view
        returns (uint32)
    {
        return listData.list[0].next;
    }

    /// @notice Get the LimitOrder data struct for the first order
    function getTopLimitOrder(MaxLinkedList storage listData)
        public
        view
        returns (LimitOrder storage)
    {
        require(!isEmpty(listData), "Book side is empty");
        return listData.idToLimitOrder[getFirstNode(listData)];
    }

    /// @notice Returns true if the linked list has no orders
    /// @dev Order id 0 and 1 are dummy values, so the linked list
    /// is empty if those are the only two orders
    function isEmpty(MaxLinkedList storage listData)
        public
        view
        returns (bool)
    {
        return getFirstNode(listData) == 1;
    }

    /// @notice Returns the number of orders in the linked list
    /// @dev Order id 0 and 1 are dummy values, so the number of
    /// orders does not include them
    function size(MaxLinkedList storage listData) public view returns (uint32) {
        uint32 listSize = 0;
        for (
            uint32 pointer = getFirstNode(listData);
            pointer != 1;
            pointer = listData.list[pointer].next
        ) ++listSize;
        return listSize;
    }

    /// @notice Returns a list of LimitOrder data structs for each
    /// order in the linked list
    /// @dev Order id 0 and 1 are dummy values, so the returned list
    /// does not include them
    function getOrders(MaxLinkedList storage listData)
        public
        view
        returns (LimitOrder[] memory orders)
    {
        orders = new LimitOrder[](size(listData));
        uint32 i = 0;
        for (
            uint32 pointer = getFirstNode(listData);
            pointer != 1;
            pointer = listData.list[pointer].next
        ) {
            orders[i] = listData.idToLimitOrder[pointer];
            ++i;
        }
    }

    /// @notice Comparison function for linked list. Returns true if the
    /// price of amount0 to amount1 is greater than the price of order id1
    function mockCompare(
        MaxLinkedList storage listData,
        uint256 amount0,
        uint256 amount1,
        uint32 id1
    ) internal view returns (bool) {
        return
            FullMath.mulCompare(
                listData.idToLimitOrder[id1].amount1,
                amount0,
                listData.idToLimitOrder[id1].amount0,
                amount1
            );
    }

    /// @notice Find the order id to the left of where the new order
    /// should be inserted. Meant to be used off-chain to find the
    /// hintId for the insert function
    /// @param amount0 The amount of token0 in the new order
    /// @param amount1 The amount of token1 in the new order
    function getMockIndexToInsert(
        MaxLinkedList storage listData,
        uint256 amount0,
        uint256 amount1
    ) public view returns (uint32) {
        uint32 hintId = 0;

        // After the two while loops, hintId will be the order id to the
        // left of where the new order should be inserted.

        while (hintId != 1) {
            uint32 nextId = listData.list[hintId].next;
            if (mockCompare(listData, amount0, amount1, nextId)) break;
            hintId = nextId;
        }

        while (hintId != 0) {
            uint32 prevId = listData.list[hintId].prev;
            if (!mockCompare(listData, amount0, amount1, hintId)) break;
            hintId = prevId;
        }

        return hintId;
    }
}

File 10 of 12 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

File 11 of 12 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 12 of 12 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"factoryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint32","name":"orderId","type":"uint32"}],"name":"cancelLimitOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint8","name":"size","type":"uint8"},{"internalType":"uint32[]","name":"orderId","type":"uint32[]"}],"name":"cancelLimitOrderBatch","outputs":[{"internalType":"bool[]","name":"isCanceled","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"claimBaseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"claimQuoteToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"address","name":"owner","type":"address"}],"name":"claimableBaseToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"address","name":"owner","type":"address"}],"name":"claimableQuoteToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint64","name":"amount0Base","type":"uint64"},{"internalType":"uint64","name":"priceBase","type":"uint64"},{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint32","name":"hintId","type":"uint32"}],"name":"createLimitOrder","outputs":[{"internalType":"uint32","name":"orderId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint8","name":"size","type":"uint8"},{"internalType":"uint64[]","name":"amount0Base","type":"uint64[]"},{"internalType":"uint64[]","name":"priceBase","type":"uint64[]"},{"internalType":"bool[]","name":"isAsk","type":"bool[]"},{"internalType":"uint32[]","name":"hintId","type":"uint32[]"}],"name":"createLimitOrderBatch","outputs":[{"internalType":"uint32[]","name":"orderId","type":"uint32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint64","name":"amount0Base","type":"uint64"},{"internalType":"uint64","name":"priceBase","type":"uint64"},{"internalType":"bool","name":"isAsk","type":"bool"}],"name":"createMarketOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"getBestAsk","outputs":[{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"internalType":"struct LimitOrder","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"getBestBid","outputs":[{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"internalType":"struct LimitOrder","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"getLimitOrders","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"},{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bool","name":"isAsk","type":"bool"}],"name":"getMockIndexToInsert","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"tokenToTransferFrom","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"orderBookId","type":"uint8"}],"name":"subtractSafeBalanceCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"internalType":"uint64","name":"newAmount0Base","type":"uint64"},{"internalType":"uint64","name":"newPriceBase","type":"uint64"},{"internalType":"uint32","name":"hintId","type":"uint32"}],"name":"updateLimitOrder","outputs":[{"internalType":"uint32","name":"newOrderId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"orderBookId","type":"uint8"},{"internalType":"uint8","name":"size","type":"uint8"},{"internalType":"uint32[]","name":"orderId","type":"uint32[]"},{"internalType":"uint64[]","name":"newAmount0Base","type":"uint64[]"},{"internalType":"uint64[]","name":"newPriceBase","type":"uint64[]"},{"internalType":"uint32[]","name":"hintId","type":"uint32[]"}],"name":"updateLimitOrderBatch","outputs":[{"internalType":"uint32[]","name":"newOrderId","type":"uint32[]"}],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b506040516200297138038062002971833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b6080516128d66200009b60003960008181610736015261190001526128d66000f3fe608060405234801561001057600080fd5b50600436106100eb5760003560e01c80639ffb45db116100925780639ffb45db1461070b578063baa0c0551461071e578063c45a015514610731578063cb831f5714610765578063ccbb495614610786578063d3a2010414610799578063d5801322146107ac578063d965ce64146107bf578063ed67ea45146107d2576100eb565b806331a3abb4146105e95780633e5be79d146106115780635b3e4eb9146106245780637aa582c11461064457806380bb1c03146106995780638789aac3146106c15780639100f58f146106e55780639624e889146106f8575b60006100f9600060016107f2565b905060006101086001806107f2565b90506000610118600260016107f2565b90506003600060ff83166001600160401b0381111561013957610139611c2b565b604051908082528060200260200182016040528015610162578160200160208202803683370190505b50905060008360ff166001600160401b0381111561018257610182611c2b565b6040519080825280602002602001820160405280156101ab578160200160208202803683370190505b50905060008460ff166001600160401b038111156101cb576101cb611c2b565b6040519080825280602002602001820160405280156101f4578160200160208202803683370190505b50905060008560ff166001600160401b0381111561021457610214611c2b565b60405190808252806020026020018201604052801561023d578160200160208202803683370190505b509050876001036103ef5760008660ff166001600160401b0381111561026557610265611c2b565b60405190808252806020026020018201604052801561028e578160200160208202803683370190505b5090506000805b8860ff168110156103dc576102ab8860086107f2565b8782815181106102bd576102bd611c41565b6001600160401b03909216602092830291909101909101526102ea6102e3896008611c6d565b60086107f2565b8682815181106102fc576102fc611c41565b6001600160401b0390921660209283029190910190910152610329610322896010611c6d565b60016107f2565b915060018260ff1611156103585760405162461bcd60e51b815260040161034f90611c80565b60405180910390fd5b8160ff1660011483828151811061037157610371611c41565b9115156020928302919091019091015261039661038f896011611c6d565b60046107f2565b8582815181106103a8576103a8611c41565b63ffffffff909216602092830291909101909101526103c8601589611c6d565b9750806103d481611ca7565b915050610295565b506103eb8989888886896108ca565b5050505b8760020361050c5760005b8660ff168110156104fb576104108660046107f2565b82828151811061042257610422611c41565b63ffffffff909216602092830291909101909101526104456102e3876004611c6d565b85828151811061045757610457611c41565b6001600160401b039092166020928302919091019091015261047d6102e387600c611c6d565b84828151811061048f5761048f611c41565b6001600160401b03909216602092830291909101909101526104b561038f876014611c6d565b8382815181106104c7576104c7611c41565b63ffffffff909216602092830291909101909101526104e7601887611c6d565b9550806104f381611ca7565b9150506103fa565b5061050a878783878787610a60565b505b876003036105815760005b8660ff168110156105735761052d8660046107f2565b82828151811061053f5761053f611c41565b63ffffffff9092166020928302919091019091015261055f600487611c6d565b95508061056b81611ca7565b915050610517565b5061057f878783610ddc565b505b876004036105e7576000610597601260016107f2565b905060018160ff1611156105bd5760405162461bcd60e51b815260040161034f90611c80565b6105e5886105cd600260086107f2565b6105d9600a60086107f2565b8460ff16600114610f07565b505b005b6105fc6105f7366004611ceb565b610f94565b60405190151581526020015b60405180910390f35b6105e761061f366004611d22565b61101c565b610637610632366004611e7d565b610a60565b6040516106089190611f8a565b610657610652366004611d22565b61108c565b6040516106089190815163ffffffff1681526020808301516001600160a01b031690820152604080830151908201526060918201519181019190915260800190565b6106ac6106a7366004611fab565b61110a565b60405163ffffffff9091168152602001610608565b6106d46106cf366004611d22565b61119b565b604051610608959493929190612055565b6105e76106f3366004611d22565b611229565b610637610706366004612156565b6108ca565b6106ac6107193660046121f2565b611263565b6105e761072c36600461225d565b610f07565b6107587f000000000000000000000000000000000000000000000000000000000000000081565b60405161060891906122a8565b6107786107733660046122d1565b611478565b604051908152602001610608565b6107786107943660046122d1565b6114f4565b6105e76107a73660046122fd565b61152f565b6106ac6107ba36600461234e565b6117f7565b6106576107cd366004611d22565b611888565b6107e56107e036600461239b565b610ddc565b60405161060891906123f8565b60008060208311156108415760405162461bcd60e51b81526020600482015260186024820152774c656e677468206c696d697420697320333220627974657360401b604482015260640161034f565b3661084c8585611c6d565b11156108a65760405162461bcd60e51b815260206004820152602360248201527f747279696e6720746f2072656164207061737420656e64206f662063616c6c6460448201526261746160e81b606482015260840161034f565b5082356108b483600861240b565b6108c09061010061242a565b1c90505b92915050565b606060006108d7886118db565b90508660ff166001600160401b038111156108f4576108f4611c2b565b60405190808252806020026020018201604052801561091d578160200160208202803683370190505b50915060005b8760ff168160ff161015610a5457816001600160a01b031663c6297024888360ff168151811061095557610955611c41565b6020026020010151888460ff168151811061097257610972611c41565b6020026020010151888560ff168151811061098f5761098f611c41565b602002602001015133898760ff16815181106109ad576109ad611c41565b60200260200101516040518663ffffffff1660e01b81526004016109d595949392919061243d565b6020604051808303816000875af11580156109f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a18919061247c565b838260ff1681518110610a2d57610a2d611c41565b63ffffffff9092166020928302919091019091015280610a4c81612499565b915050610923565b50509695505050505050565b60608560ff166001600160401b03811115610a7d57610a7d611c2b565b604051908082528060200260200182016040528015610aa6578160200160208202803683370190505b5090506000610ab4886118db565b905060008060005b8960ff16811015610dce57836001600160a01b031663b866365a8a8381518110610ae857610ae8611c41565b60200260200101516040518263ffffffff1660e01b8152600401610b18919063ffffffff91909116815260200190565b602060405180830381865afa158015610b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5991906124b8565b610b90576000858281518110610b7157610b71611c41565b602002602001019063ffffffff16908163ffffffff1681525050610dbc565b836001600160a01b0316632cea5cd38a8381518110610bb157610bb1611c41565b60200260200101516040518263ffffffff1660e01b8152600401610be1919063ffffffff91909116815260200190565b602060405180830381865afa158015610bfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2291906124b8565b9150836001600160a01b031663a29d824c8a8381518110610c4557610c45611c41565b6020026020010151336040518363ffffffff1660e01b8152600401610c6b9291906124d5565b6020604051808303816000875af1158015610c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cae91906124b8565b925082610ccd5760405162461bcd60e51b815260040161034f906124f4565b836001600160a01b031663c6297024898381518110610cee57610cee611c41565b6020026020010151898481518110610d0857610d08611c41565b602002602001015185338b8781518110610d2457610d24611c41565b60200260200101516040518663ffffffff1660e01b8152600401610d4c95949392919061243d565b6020604051808303816000875af1158015610d6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8f919061247c565b858281518110610da157610da1611c41565b602002602001019063ffffffff16908163ffffffff16815250505b80610dc681611ca7565b915050610abc565b505050509695505050505050565b60606000610de9856118db565b90508360ff166001600160401b03811115610e0657610e06611c2b565b604051908082528060200260200182016040528015610e2f578160200160208202803683370190505b50915060005b8460ff16811015610efe57816001600160a01b031663a29d824c858381518110610e6157610e61611c41565b6020026020010151336040518363ffffffff1660e01b8152600401610e879291906124d5565b6020604051808303816000875af1158015610ea6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eca91906124b8565b838281518110610edc57610edc611c41565b9115156020928302919091019091015280610ef681611ca7565b915050610e35565b50509392505050565b6000610f12856118db565b604051638e9d59cd60e01b81526001600160401b0380871660048301528516602482015283151560448201523360648201529091506001600160a01b03821690638e9d59cd90608401600060405180830381600087803b158015610f7557600080fd5b505af1158015610f89573d6000803e3d6000fd5b505050505050505050565b600080610fa0846118db565b6040516328a7609360e21b81529091506001600160a01b0382169063a29d824c90610fd190869033906004016124d5565b6020604051808303816000875af1158015610ff0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101491906124b8565b949350505050565b6000611027826118db565b60405163b8005df360e01b81529091506001600160a01b0382169063b8005df3906110569033906004016122a8565b600060405180830381600087803b15801561107057600080fd5b505af1158015611084573d6000803e3d6000fd5b505050505050565b611094611bf4565b600061109f836118db565b9050806001600160a01b0316632225cf316040518163ffffffff1660e01b8152600401608060405180830381865afa1580156110df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611103919061252b565b9392505050565b600080611116866118db565b604051631a242bd760e11b8152600481018790526024810186905284151560448201529091506001600160a01b0382169063344857ae90606401602060405180830381865afa15801561116d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611191919061247c565b9695505050505050565b606080606080606060006111ae876118db565b9050806001600160a01b0316633e65db3e6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156111ee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112169190810190612726565b939b929a50909850965090945092505050565b6000611234826118db565b604051631358f10560e31b81529091506001600160a01b03821690639ac78828906110569033906004016122a8565b60008061126f876118db565b604051635c331b2d60e11b815263ffffffff881660048201529091506001600160a01b0382169063b866365a90602401602060405180830381865afa1580156112bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e091906124b8565b6112ed576000915061146e565b604051632cea5cd360e01b815263ffffffff871660048201526000906001600160a01b03831690632cea5cd390602401602060405180830381865afa15801561133a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135e91906124b8565b90506000826001600160a01b031663a29d824c89336040518363ffffffff1660e01b81526004016113909291906124d5565b6020604051808303816000875af11580156113af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d391906124b8565b9050806113f25760405162461bcd60e51b815260040161034f906124f4565b60405163318a5c0960e21b81526001600160a01b0384169063c629702490611426908a908a90879033908c9060040161243d565b6020604051808303816000875af1158015611445573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611469919061247c565b935050505b5095945050505050565b600080611484846118db565b6040516374f1e85560e11b81529091506001600160a01b0382169063e9e3d0aa906114b39086906004016122a8565b602060405180830381865afa1580156114d0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101491906127f7565b600080611500846118db565b604051634394ea2560e11b81529091506001600160a01b03821690638729d44a906114b39086906004016122a8565b611538816118db565b6001600160a01b0316336001600160a01b0316146115985760405162461bcd60e51b815260206004820181905260248201527f43616c6c657220646f6573206e6f74206d61746368206f7264657220626f6f6b604482015260640161034f565b6040516370a0823160e01b81526000906001600160a01b038616906370a08231906115c79087906004016122a8565b602060405180830381865afa1580156115e4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160891906127f7565b9050808311156116765760405162461bcd60e51b815260206004820152603360248201527f496e73756666696369656e742066756e6473206173736f63696174656420776960448201527274682073656e6465722773206164647265737360681b606482015260840161034f565b6040516370a0823160e01b81526000906001600160a01b038716906370a08231906116a59033906004016122a8565b602060405180830381865afa1580156116c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e691906127f7565b90506116fd6001600160a01b0387168633876119b9565b6040516370a0823160e01b81526000906001600160a01b038816906370a082319061172c9033906004016122a8565b602060405180830381865afa158015611749573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176d91906127f7565b90506117798583611c6d565b8110156117ee5760405162461bcd60e51b815260206004820152603c60248201527f4f7264657220626f6f6b2062616c616e6365206368616e676520646f6573206e60448201527f6f74206d617463682074686520726563656976656420616d6f756e7400000000606482015260840161034f565b50505050505050565b600080611803876118db565b60405163318a5c0960e21b81529091506001600160a01b0382169063c62970249061183a9089908990899033908a9060040161243d565b6020604051808303816000875af1158015611859573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187d919061247c565b979650505050505050565b611890611bf4565b600061189b836118db565b9050806001600160a01b03166398121e166040518163ffffffff1660e01b8152600401608060405180830381865afa1580156110df573d6000803e3d6000fd5b60405163312edd6960e21b815260ff8216600482015260009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c4bb75a490602401602060405180830381865afa158015611947573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196b9190612810565b90506001600160a01b0381166108c45760405162461bcd60e51b8152602060048201526013602482015272125b9d985b1a59081bdc99195c909bdbdad259606a1b604482015260640161034f565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611a13908590611a19565b50505050565b6000611a6e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611af09092919063ffffffff16565b805190915015611aeb5780806020019051810190611a8c91906124b8565b611aeb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161034f565b505050565b60606110148484600085856001600160a01b0385163b611b525760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161034f565b600080866001600160a01b03168587604051611b6e9190612851565b60006040518083038185875af1925050503d8060008114611bab576040519150601f19603f3d011682016040523d82523d6000602084013e611bb0565b606091505b509150915061187d82828660608315611bca575081611103565b825115611bda5782518084602001fd5b8160405162461bcd60e51b815260040161034f919061286d565b6040518060800160405280600063ffffffff16815260200160006001600160a01b0316815260200160008152602001600081525090565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b808201808211156108c4576108c4611c57565b6020808252600d908201526c496e76616c696420697341736b60981b604082015260600190565b600060018201611cb957611cb9611c57565b5060010190565b803560ff81168114611cd157600080fd5b919050565b63ffffffff81168114611ce857600080fd5b50565b60008060408385031215611cfe57600080fd5b611d0783611cc0565b91506020830135611d1781611cd6565b809150509250929050565b600060208284031215611d3457600080fd5b61110382611cc0565b604051601f8201601f191681016001600160401b0381118282101715611d6557611d65611c2b565b604052919050565b60006001600160401b03821115611d8657611d86611c2b565b5060051b60200190565b600082601f830112611da157600080fd5b81356020611db6611db183611d6d565b611d3d565b82815260059290921b84018101918181019086841115611dd557600080fd5b8286015b84811015611df9578035611dec81611cd6565b8352918301918301611dd9565b509695505050505050565b80356001600160401b0381168114611cd157600080fd5b600082601f830112611e2c57600080fd5b81356020611e3c611db183611d6d565b82815260059290921b84018101918181019086841115611e5b57600080fd5b8286015b84811015611df957611e7081611e04565b8352918301918301611e5f565b60008060008060008060c08789031215611e9657600080fd5b611e9f87611cc0565b9550611ead60208801611cc0565b945060408701356001600160401b0380821115611ec957600080fd5b611ed58a838b01611d90565b95506060890135915080821115611eeb57600080fd5b611ef78a838b01611e1b565b94506080890135915080821115611f0d57600080fd5b611f198a838b01611e1b565b935060a0890135915080821115611f2f57600080fd5b50611f3c89828a01611d90565b9150509295509295509295565b600081518084526020808501945080840160005b83811015611f7f57815163ffffffff1687529582019590820190600101611f5d565b509495945050505050565b6020815260006111036020830184611f49565b8015158114611ce857600080fd5b60008060008060808587031215611fc157600080fd5b611fca85611cc0565b935060208501359250604085013591506060850135611fe881611f9d565b939692955090935050565b600081518084526020808501945080840160005b83811015611f7f57815187529582019590820190600101612007565b600081518084526020808501945080840160005b83811015611f7f578151151587529582019590820190600101612037565b60a08152600061206860a0830188611f49565b82810360208481019190915287518083528882019282019060005b818110156120a85784516001600160a01b031683529383019391830191600101612083565b505084810360408601526120bc8189611ff3565b9250505082810360608401526120d28186611ff3565b905082810360808401526120e68185612023565b98975050505050505050565b600082601f83011261210357600080fd5b81356020612113611db183611d6d565b82815260059290921b8401810191818101908684111561213257600080fd5b8286015b84811015611df957803561214981611f9d565b8352918301918301612136565b60008060008060008060c0878903121561216f57600080fd5b61217887611cc0565b955061218660208801611cc0565b945060408701356001600160401b03808211156121a257600080fd5b6121ae8a838b01611e1b565b955060608901359150808211156121c457600080fd5b6121d08a838b01611e1b565b945060808901359150808211156121e657600080fd5b611f198a838b016120f2565b600080600080600060a0868803121561220a57600080fd5b61221386611cc0565b9450602086013561222381611cd6565b935061223160408701611e04565b925061223f60608701611e04565b9150608086013561224f81611cd6565b809150509295509295909350565b6000806000806080858703121561227357600080fd5b61227c85611cc0565b935061228a60208601611e04565b925061229860408601611e04565b91506060850135611fe881611f9d565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114611ce857600080fd5b600080604083850312156122e457600080fd5b6122ed83611cc0565b91506020830135611d17816122bc565b6000806000806080858703121561231357600080fd5b843561231e816122bc565b9350602085013561232e816122bc565b92506040850135915061234360608601611cc0565b905092959194509250565b600080600080600060a0868803121561236657600080fd5b61236f86611cc0565b945061237d60208701611e04565b935061238b60408701611e04565b9250606086013561223f81611f9d565b6000806000606084860312156123b057600080fd5b6123b984611cc0565b92506123c760208501611cc0565b915060408401356001600160401b038111156123e257600080fd5b6123ee86828701611d90565b9150509250925092565b6020815260006111036020830184612023565b600081600019048311821515161561242557612425611c57565b500290565b818103818111156108c4576108c4611c57565b6001600160401b03958616815293909416602084015290151560408301526001600160a01b0316606082015263ffffffff909116608082015260a00190565b60006020828403121561248e57600080fd5b815161110381611cd6565b600060ff821660ff81036124af576124af611c57565b60010192915050565b6000602082840312156124ca57600080fd5b815161110381611f9d565b63ffffffff9290921682526001600160a01b0316602082015260400190565b6020808252601a908201527f436f756c64206e6f742063616e63656c20746865206f72646572000000000000604082015260600190565b60006080828403121561253d57600080fd5b604051608081018181106001600160401b038211171561255f5761255f611c2b565b604052825161256d81611cd6565b8152602083015161257d816122bc565b6020820152604083810151908201526060928301519281019290925250919050565b600082601f8301126125b057600080fd5b815160206125c0611db183611d6d565b82815260059290921b840181019181810190868411156125df57600080fd5b8286015b84811015611df95780516125f681611cd6565b83529183019183016125e3565b600082601f83011261261457600080fd5b81516020612624611db183611d6d565b82815260059290921b8401810191818101908684111561264357600080fd5b8286015b84811015611df957805161265a816122bc565b8352918301918301612647565b600082601f83011261267857600080fd5b81516020612688611db183611d6d565b82815260059290921b840181019181810190868411156126a757600080fd5b8286015b84811015611df957805183529183019183016126ab565b600082601f8301126126d357600080fd5b815160206126e3611db183611d6d565b82815260059290921b8401810191818101908684111561270257600080fd5b8286015b84811015611df957805161271981611f9d565b8352918301918301612706565b600080600080600060a0868803121561273e57600080fd5b85516001600160401b038082111561275557600080fd5b61276189838a0161259f565b9650602088015191508082111561277757600080fd5b61278389838a01612603565b9550604088015191508082111561279957600080fd5b6127a589838a01612667565b945060608801519150808211156127bb57600080fd5b6127c789838a01612667565b935060808801519150808211156127dd57600080fd5b506127ea888289016126c2565b9150509295509295909350565b60006020828403121561280957600080fd5b5051919050565b60006020828403121561282257600080fd5b8151611103816122bc565b60005b83811015612848578181015183820152602001612830565b50506000910152565b6000825161286381846020870161282d565b9190910192915050565b602081526000825180602084015261288c81604085016020870161282d565b601f01601f1916919091016040019291505056fea264697066735822122027c4d65e1b8004adbb370b6ef6f10d67b32e058660f8d30b74309f1390a28f2164736f6c6343000810003300000000000000000000000035642792abc96fa1e9ffe5f2f62a539bb80a8af4

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000035642792abc96fa1e9ffe5f2f62a539bb80a8af4

-----Decoded View---------------
Arg [0] : factoryAddress (address): 0x35642792abc96fa1e9ffe5f2f62a539bb80a8af4

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000035642792abc96fa1e9ffe5f2f62a539bb80a8af4


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.