ETH Price: $1,798.48 (+10.64%)

Contract

0xCF56f4a7C8Ce42c92B93b6D4a814703E7cb7aE34

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Submit Order Wit...217022922025-04-22 15:31:5717 hrs ago1745335917IN
0xCF56f4a7...E7cb7aE34
0.0382216 ETH0.000070440.2016
Submit Order216736452025-04-21 13:48:5943 hrs ago1745243339IN
0xCF56f4a7...E7cb7aE34
0.05987147 ETH0.000008570.012
Cancel Order216735892025-04-21 13:45:4543 hrs ago1745243145IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000390.012
Submit Order216567212025-04-20 22:12:092 days ago1745187129IN
0xCF56f4a7...E7cb7aE34
0.11063 ETH0.000008570.012
Submit Order216565702025-04-20 22:03:502 days ago1745186630IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000003450.012
Cancel Order216551502025-04-20 20:46:132 days ago1745181973IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000710.012
Submit Order216547672025-04-20 20:25:122 days ago1745180712IN
0xCF56f4a7...E7cb7aE34
0.05950243 ETH0.000008820.012
Cancel Order216547592025-04-20 20:24:452 days ago1745180685IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000650.012
Cancel Order216545592025-04-20 20:13:312 days ago1745180011IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000780.012
Cancel Order216544722025-04-20 20:08:432 days ago1745179723IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000780.012
Submit Order216542322025-04-20 19:55:232 days ago1745178923IN
0xCF56f4a7...E7cb7aE34
0.0594449 ETH0.000008770.012
Submit Order216539722025-04-20 19:41:052 days ago1745178065IN
0xCF56f4a7...E7cb7aE34
0.05987253 ETH0.000008820.012
Submit Order215685232025-04-17 13:16:065 days ago1744895766IN
0xCF56f4a7...E7cb7aE34
0.06291937 ETH0.000008820.012
Submit Order215558422025-04-17 1:41:366 days ago1744854096IN
0xCF56f4a7...E7cb7aE34
0.06301957 ETH0.000008820.012
Submit Order Wit...214906532025-04-14 13:55:598 days ago1744638959IN
0xCF56f4a7...E7cb7aE34
0.06477208 ETH0.000050210.06564
Cancel Order214692712025-04-13 18:16:499 days ago1744568209IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000390.012
Cancel Order214692712025-04-13 18:16:499 days ago1744568209IN
0xCF56f4a7...E7cb7aE34
0 ETH0.000000870.012
Submit Order214691932025-04-13 18:12:299 days ago1744567949IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000004330.012
Submit Order213417742025-04-08 21:43:1814 days ago1744148598IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000003650.012
Submit Order Wit...213406502025-04-08 20:41:4114 days ago1744144901IN
0xCF56f4a7...E7cb7aE34
0.0003 ETH0.000009610.012
Submit Order212622942025-04-05 21:04:3717 days ago1743887077IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000000570.012
Submit Order212622822025-04-05 21:03:5717 days ago1743887037IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000003650.012
Submit Order212621742025-04-05 20:58:0217 days ago1743886682IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000004330.012
Submit Order211726952025-04-02 11:26:1920 days ago1743593179IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000003650.012
Submit Order211725482025-04-02 11:18:2120 days ago1743592701IN
0xCF56f4a7...E7cb7aE34
0.0001 ETH0.000004330.012

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
217022922025-04-22 15:31:5717 hrs ago1745335917
0xCF56f4a7...E7cb7aE34
0.0382216 ETH
216736452025-04-21 13:48:5943 hrs ago1745243339
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216736452025-04-21 13:48:5943 hrs ago1745243339
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216736452025-04-21 13:48:5943 hrs ago1745243339
0xCF56f4a7...E7cb7aE34
0.05967147 ETH
216567212025-04-20 22:12:092 days ago1745187129
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216567212025-04-20 22:12:092 days ago1745187129
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216567212025-04-20 22:12:092 days ago1745187129
0xCF56f4a7...E7cb7aE34
0.11043 ETH
216565702025-04-20 22:03:502 days ago1745186630
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216547672025-04-20 20:25:122 days ago1745180712
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216547672025-04-20 20:25:122 days ago1745180712
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216547672025-04-20 20:25:122 days ago1745180712
0xCF56f4a7...E7cb7aE34
0.05930243 ETH
216542322025-04-20 19:55:232 days ago1745178923
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216542322025-04-20 19:55:232 days ago1745178923
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216542322025-04-20 19:55:232 days ago1745178923
0xCF56f4a7...E7cb7aE34
0.0592449 ETH
216539722025-04-20 19:41:052 days ago1745178065
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216539722025-04-20 19:41:052 days ago1745178065
0xCF56f4a7...E7cb7aE34
0.0001 ETH
216539722025-04-20 19:41:052 days ago1745178065
0xCF56f4a7...E7cb7aE34
0.05967253 ETH
215685232025-04-17 13:16:065 days ago1744895766
0xCF56f4a7...E7cb7aE34
0.0001 ETH
215685232025-04-17 13:16:065 days ago1744895766
0xCF56f4a7...E7cb7aE34
0.0001 ETH
215685232025-04-17 13:16:065 days ago1744895766
0xCF56f4a7...E7cb7aE34
0.06271937 ETH
215558422025-04-17 1:41:366 days ago1744854096
0xCF56f4a7...E7cb7aE34
0.0001 ETH
215558422025-04-17 1:41:366 days ago1744854096
0xCF56f4a7...E7cb7aE34
0.0001 ETH
215558422025-04-17 1:41:366 days ago1744854096
0xCF56f4a7...E7cb7aE34
0.06281957 ETH
214906532025-04-14 13:55:598 days ago1744638959
0xCF56f4a7...E7cb7aE34
0.0001 ETH
214906532025-04-14 13:55:598 days ago1744638959
0xCF56f4a7...E7cb7aE34
0.0001 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OrderBook

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 25 : OrderBook.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import '@openzeppelin/contracts/utils/math/SafeCast.sol';


import './utils/AddressStorage.sol';
import './Store.sol';
import './utils/interfaces/IStore.sol';
import './utils/TradingValidator.sol';
import './PositionManager.sol';
import './interfaces/IReferralStorage.sol';

import './utils/Governable.sol';

/**
 * @title  OrderBook
 * @notice Implementation of order related logic, i.e. submitting orders / cancelling them
 */
contract OrderBook is Governable {
    // Libraries
    using EnumerableSet for EnumerableSet.UintSet;
    using Address for address payable;
    using SafeCast for uint256;


    // Constants
    uint256 public constant UNIT = 10 ** 18;
    uint256 public constant BPS_DIVIDER = 10000;
    uint256 public constant MAX_TRAILING_STOP_PERCENTAGE = 2000;

    // Order struct

    struct OrderDetail{ 
        uint8 orderType; // 0 = market, 1 = limit, 2 = stop , 3 = trailing stop   
        bool isReduceOnly; // Whether the order is reduce-only
        uint96 price; // The order's price if its a trigger or protected order 
        uint32 expiry; // block.timestamp at which the order expires           
        uint32 cancelOrderId; // orderId to cancel when this order executes 
        uint64 executionFee; //Fee paid with native token for keeper's execution
        uint16 trailingStopPercentage; //Trailing stop percentage in bps e.g. 200 for %2
    }

    struct Order { 
        address user; // user that submitted the order 
        uint96 margin; // Collateral tied to this order. In wei 
        address asset; // Asset address, e.g. address(0) for ETH 
        bytes10 market; // Market this order was submitted on     
        bool isLong; // Whether the order is a buy or sell order
        uint96 size; // Order size (margin * leverage). In wei
        uint96 fee; // Fee amount paid. In wei
        uint32 timestamp; // block.timestamp at which the order was submitted
        uint32 orderId;  // incremental order id 
        OrderDetail orderDetail; 
    }

    uint32 public oid; // incremental order id
    mapping(uint32 => Order) private orders; // order id => Order
    mapping(address => EnumerableSet.UintSet) private userOrderIds; // user => [order ids..]
    EnumerableSet.UintSet private marketOrderIds; // [order ids..]
    EnumerableSet.UintSet private triggerOrderIds; // [order ids..]

    uint256 public maxMarketOrderTTL = 5 minutes;  // duration until market orders expire
    uint256 public maxTriggerOrderTTL = 180 days;  // duration until trigger orders expire

    uint64 public orderExecutionFee;  //  fee with native token required for the execution of the order by keepers

    bool public areNewOrdersPaused;
    bool public isProcessingPaused;

    bytes32 public ethSignedMessageHash;  // Message Hash equivalent of UI  sign message of enable orders, remix.ethereum.org can be used for message hash

    mapping(address => bool) public whitelistedFundingAccount;  // accounts authorized to open positions on behalf of another user

    mapping(address => bool) public approvedAccounts; // accounts approved to submit order

    // Events

    // Order of function / event params: id, user, asset, market
    event OrderCreated(
        uint32 indexed orderId,
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint96 margin,
        uint96 size,
        uint96 price,
        uint96 fee,
        bool isLong,
        uint8 orderType,
        bool isReduceOnly,
        uint32 expiry,
        uint32 cancelOrderId,
        address fundingAccount,
        uint64 executionFee,
        uint16 trailingStopPercentage
    );

    event OrderCancelled(uint32 indexed orderId, address indexed user, address executionFeeReceiver, string reason);
    event AccountApproved(address indexed user, bool signed, bool byGov);
    event EthSignedMessageHashUpdated(bytes32 ethSignedMessageHash);  
    event WhitelistedFundingAccountUpdated(address indexed account, bool isActive);
    event MaxMarketOrderTTLUpdated(uint256 maxMarketOrderTTL);
    event MaxTriggerOrderTTLUpdated(uint256 maxTriggerOrderTTL);
    event OrderExecutionFeeUpdated(uint64 orderExecutionFee);
    event Link(address store, address tradingValidator, address referralStorage, address executor, address positionManager);
    event NewOrdersPaused(bool orderPaused);
    event ProcessingPaused(bool processingPaused);

    event AddOrder(uint32 indexed orderId, uint8 orderType);
    event RemoveOrder(uint32 indexed orderId, uint8 orderType);

    // Contracts
    AddressStorage public immutable addressStorage;
    Store public store;
    PositionManager public positionManager;
    TradingValidator public tradingValidator;
    IReferralStorage public referralStorage;

    address public executorAddress;

    /// @dev Reverts if order processing is paused
    modifier ifNotPaused() {
        require(!areNewOrdersPaused, '!paused');
        _;
    }

    /// @dev Only callable by Executor contract
    modifier onlyExecutor() {
        require(msg.sender == executorAddress, "!unauthorized");
        _;
    }

    /// @dev Only callable by PositionManager contract
    modifier onlyPositionManager() {
        require(msg.sender == address(positionManager), "!unauthorized");
        _;
    }

    /// @dev Initializes addressStorage address
    constructor(AddressStorage _addressStorage)  {
        addressStorage = _addressStorage;
    }

    /// @notice Set EthSignedMessageHash
    /// @dev Only callable by governance
    /// @param _messageHash Message Hash,remix.ethereum.org can be used 
    function setEthSignedMessageHash(bytes32 _messageHash) external onlyGov {
        ethSignedMessageHash = _messageHash;
        emit EthSignedMessageHashUpdated(_messageHash);
    }

    /// @notice Set whitelisted Funding Account
    /// @dev Only callable by governance
    /// @param _account  accounts authorized to open positions on behalf of another user
    /// @param _isActive whether account is active
    function setWhitelistedFundingAccount(address _account, bool _isActive) external onlyGov {
        whitelistedFundingAccount[_account] = _isActive;
        emit WhitelistedFundingAccountUpdated(_account,_isActive);
    }

    /// @notice Disable submitting new orders
    /// @dev Only callable by governance
    function setAreNewOrdersPaused(bool _areNewOrdersPaused) external onlyGov {
        areNewOrdersPaused = _areNewOrdersPaused;
        emit NewOrdersPaused(areNewOrdersPaused);
    }

    /// @notice Disable processing new orders
    /// @dev Only callable by governance
    function setIsProcessingPaused(bool _isProcessingPaused) external onlyGov {
        isProcessingPaused = _isProcessingPaused;
        emit ProcessingPaused(isProcessingPaused);
    }

    /// @notice Set duration until market orders expire
    /// @dev Only callable by governance
    /// @param _maxMarketOrderTTL Duration in seconds
    function setMaxMarketOrderTTL(uint256 _maxMarketOrderTTL) external onlyGov {
        require(_maxMarketOrderTTL > 0, '!gt_zero'); // greater than zero
        require(_maxMarketOrderTTL < maxTriggerOrderTTL, '!lt_triggerttl'); // must be less than trigger ttl value
        maxMarketOrderTTL = _maxMarketOrderTTL;
        emit MaxMarketOrderTTLUpdated(_maxMarketOrderTTL);
    }

    /// @notice Set duration until trigger orders expire
    /// @dev Only callable by governance
    /// @param _maxTriggerOrderTTL Duration in seconds
    function setMaxTriggerOrderTTL(uint256 _maxTriggerOrderTTL) external onlyGov {
        require(_maxTriggerOrderTTL > 0, '!gtzero'); // greater than zero
        require(_maxTriggerOrderTTL > maxMarketOrderTTL, '!gt_marketttl'); // must be greater than market ttl value
        maxTriggerOrderTTL = _maxTriggerOrderTTL;
        emit MaxTriggerOrderTTLUpdated(_maxTriggerOrderTTL);
    }

    /// @notice Set keeper gas fee for order execution 
    /// @dev Only callable by governance
    /// @param _orderExecutionFee Fee with native token e.g. ETH
    function setOrderExecutionFee(uint64 _orderExecutionFee) external onlyGov {
        orderExecutionFee = _orderExecutionFee;
        emit OrderExecutionFeeUpdated(_orderExecutionFee);
    }

    /// @notice Enable order with onchain function
    function enableOrder() external {
        approvedAccounts[msg.sender] = true;
        emit AccountApproved(msg.sender,false,false);
    }   

    /// @notice Enable order function for accounts like contract
    /// @dev Only callable by governance
    /// @param _account EOA or contract account
    function enableOrderByGov(address _account) external onlyGov{
        approvedAccounts[_account] = true;
        emit AccountApproved(msg.sender, false, true);
    }   

    /// @notice Initializes protocol contracts
    /// @dev Only callable by governance
    function link() external onlyGov {
        store = Store(payable(addressStorage.getAddress('Store')));
        tradingValidator = TradingValidator(addressStorage.getAddress('TradingValidator'));
        referralStorage = IReferralStorage(addressStorage.getAddress('ReferralStorage'));
        executorAddress = addressStorage.getAddress('Executor');
        positionManager = PositionManager(addressStorage.getAddress('PositionManager'));
        emit Link(
            address(store),
            address(tradingValidator),
            address(referralStorage),
            executorAddress,
            address(positionManager)
        );
    }

    /// @notice Submits a new order with signature, 
    /// @dev will be called from the UI only the first time because of gas saving
    /// @param _params Order to submit
    /// @param _tpPrice 18 decimal take profit price
    /// @param _slPrice 18 decimal stop loss price
    /// @param _trailingStopPercentage trailing stop percentage 
    /// @param _referralCode code for referral system    
    /// @param _signature user signature for enabling order
    function submitOrderWithSignature(
        Order memory _params,
        uint96 _tpPrice,
        uint96 _slPrice,
        uint16 _trailingStopPercentage,
        bytes32 _referralCode,
        bytes memory _signature
    ) external payable ifNotPaused {
        require(approvedAccounts[msg.sender] || SignatureChecker.isValidSignatureNow(msg.sender,ethSignedMessageHash, _signature),"!not signed");
        if(!approvedAccounts[msg.sender]){
            approvedAccounts[msg.sender] = true;
            emit AccountApproved(msg.sender, true, false);
        }

        _submitOrder(_params,_tpPrice,_slPrice,_trailingStopPercentage,_referralCode);
    }      


    /// @notice Submits a new order
    /// @param _params Order to submit
    /// @param _tpPrice 18 decimal take profit price
    /// @param _slPrice 18 decimal stop loss price
    /// @param _trailingStopPercentage trailing stop percentage 
    /// @param _referralCode code for referral system    
    function submitOrder(
        Order memory _params,
        uint96 _tpPrice,
        uint96 _slPrice,
        uint16 _trailingStopPercentage,
        bytes32 _referralCode
    ) external payable ifNotPaused {
        require(approvedAccounts[msg.sender]  ,"!not approved");
        _submitOrder(_params,_tpPrice,_slPrice,_trailingStopPercentage,_referralCode);
    }      

    /// @notice Submits a new order
    /// @dev Internal function invoked by {submitOrderWithSignature,submitOrder} 
    function _submitOrder(
        Order memory _params,
        uint96 _tpPrice,
        uint96 _slPrice,
        uint16 _trailingStopPercentage,
        bytes32 _referralCode
    ) internal {
        // order cant be reduce-only if take profit or stop loss order is submitted alongside main order
        if (_tpPrice > 0 || _slPrice > 0 || _trailingStopPercentage > 0) {
            _params.orderDetail.isReduceOnly = false;
        }
        
        if (_params.orderDetail.isReduceOnly || _params.orderDetail.orderType == 3){
            if(_params.orderDetail.orderType == 3){
                require(_params.orderDetail.isReduceOnly , '!trailing-stop-must-reduce');
                _params.orderDetail.price = 0; // must be zero to distinguish it from a limit order ts
            }
            PositionManager.Position memory position = positionManager.getPosition(msg.sender, _params.asset, _params.market);
            require(position.size > 0 , '!no-position');
            require(position.isLong != _params.isLong , '!reduce-wrong-direction');
        }

        _params.user = _orderUser(_params);
        if(_params.user != msg.sender){
            _params.orderDetail.cancelOrderId = 0;
        }
        _params.orderDetail.executionFee = orderExecutionFee;

        uint256 totalExecutionFee = _params.orderDetail.executionFee;

        // Submit order
        uint256 valueConsumed;
        (, valueConsumed) = _submitOrder(_params);

        if (_referralCode != bytes32(0) && address(referralStorage) != address(0)) {
            referralStorage.setTraderReferralCode(_params.user, _referralCode);
        }

        // tp/sl price checks
        if (_tpPrice > 0 || _slPrice > 0 || _trailingStopPercentage > 0) {
            if (_params.orderDetail.price > 0 ) {
                if (_tpPrice > 0) {
                    require(
                        (_params.isLong && _tpPrice > _params.orderDetail.price) || (!_params.isLong && _tpPrice < _params.orderDetail.price),
                        '!tp-invalid'
                    );
                }
                if (_slPrice > 0 ) {
                    require(
                        (_params.isLong && _slPrice < _params.orderDetail.price) || (!_params.isLong && _slPrice > _params.orderDetail.price),
                        '!sl-invalid'
                    );
                }
            }

            if (_tpPrice > 0 && _slPrice > 0) {
                require((_params.isLong && _tpPrice > _slPrice) || (!_params.isLong && _tpPrice < _slPrice), '!tpsl-invalid');
            }

            // tp and sl order ids
            uint32 tpOrderId;
            uint32 slOrderId;

            // long -> short, short -> long for take profit / stop loss order
            _params.isLong = !_params.isLong;

            // reset order expiry for TP/SL orders
            if (_params.orderDetail.expiry > 0) _params.orderDetail.expiry = 0;

            // submit stop loss order
            if (_slPrice > 0 ) {
                _params.orderDetail.price = _slPrice;
                _params.orderDetail.orderType = 2;
                _params.orderDetail.isReduceOnly = true;
                totalExecutionFee += _params.orderDetail.executionFee;
                // Order is reduce-only so valueConsumed is always zero
                (slOrderId, ) = _submitOrder(_params);
            } else if (_trailingStopPercentage > 0){
                _params.orderDetail.orderType = 3;
                _params.orderDetail.isReduceOnly = true;
                _params.orderDetail.trailingStopPercentage = _trailingStopPercentage;
                totalExecutionFee += _params.orderDetail.executionFee;
                // Order is reduce-only so valueConsumed is always zero
                (slOrderId, ) = _submitOrder(_params);
            }

            // submit take profit order
            if (_tpPrice > 0) {
                _params.orderDetail.price = _tpPrice;
                _params.orderDetail.orderType = 1;
                _params.orderDetail.isReduceOnly = true;
                _params.orderDetail.trailingStopPercentage = 0;
                totalExecutionFee += _params.orderDetail.executionFee;
                // Order is reduce-only so valueConsumed is always zero
                (tpOrderId, ) = _submitOrder(_params);
            }


            // Update orders to cancel each other
            if (tpOrderId > 0 && slOrderId > 0) {
                _updateCancelOrderId(tpOrderId, slOrderId);
                _updateCancelOrderId(slOrderId, tpOrderId);
            }
        }

        uint256 requiredValue = (_params.asset == address(0) ? valueConsumed : 0) + totalExecutionFee;
        require(msg.value >= requiredValue,"!msg-value");
        uint256 diff = msg.value - requiredValue;
        if (diff > 0) {
            payable(_params.user).sendValue(diff);
        }
    }

    /// @notice Submits a new order
    /// @dev Internal function invoked by {_submitOrder} 
    function _submitOrder(Order memory _params) internal returns (uint32, uint96) {
        // Validations
        require(_params.orderDetail.orderType < 4, '!order-type');

        // execution price of trigger order cant be zero
        if (_params.orderDetail.orderType == 1 || _params.orderDetail.orderType == 2) {
            require(_params.orderDetail.price > 0, '!price');
        } else if (_params.orderDetail.orderType == 3) {
            require(_params.orderDetail.trailingStopPercentage > 0 &&  _params.orderDetail.trailingStopPercentage <= MAX_TRAILING_STOP_PERCENTAGE, '!trailing-stop-invalid');
        }

        // check if base asset is supported and order size is above min size
        IStore.Asset memory asset = store.getAsset(_params.asset);
        require(asset.minSize > 0, '!asset-exists');
        require(_params.orderDetail.isReduceOnly || _params.size >= asset.minSize, '!min-size');

        // check if market exists
        IStore.Market memory market = store.getMarket(_params.market);
        require(market.maxLeverage > 0, '!market-exists');

        // Order expiry validations
        if (_params.orderDetail.expiry > 0) {
            // expiry value cant be in the past
            require(_params.orderDetail.expiry >= block.timestamp, '!expiry-value');

            // _params.expiry cant be after default expiry of market and trigger orders
            uint256 ttl = _params.orderDetail.expiry - block.timestamp;
            if (_params.orderDetail.orderType == 0) require(ttl <= maxMarketOrderTTL, '!max-expiry');
            else require(ttl <= maxTriggerOrderTTL, '!max-expiry');
        }

        // cant cancel an order of another user
        if (_params.orderDetail.cancelOrderId > 0) {
            require(userOrderIds[_params.user].contains(_params.orderDetail.cancelOrderId), '!user-oco');
        }

        // Set timestamp
        _params.timestamp = block.timestamp.toUint32();        
        _params.fee = (_params.size * market.fee / BPS_DIVIDER).toUint96();
        uint96 valueConsumed;
        bool isSentNative;


        if (_params.orderDetail.isReduceOnly) {
            _params.margin = 0;

            // Existing position is checked on execution so TP/SL can be submitted as reduce-only alongside a non-executed order
            // In this case, valueConsumed is zero as margin is zero and fee is taken from the order's margin when position is executed
        } else {
            require(!market.isReduceOnly, '!market-reduce-only');
            require(_params.margin > 0, '!margin');

            uint256 leverage = (UNIT * _params.size) / _params.margin;
            require(leverage >= UNIT, '!min-leverage');
            require(leverage <= market.maxLeverage * UNIT, '!max-leverage');

            // Check against max OI if it's not reduce-only. this is not completely fail safe as user can place many
            // consecutive market orders of smaller size and get past the max OI limit here, because OI is not updated until
            // keeper picks up the order. That is why maxOI is checked on processing as well, which is fail safe.
            // This check is more of preemptive for user to not submit an order
            tradingValidator.checkMaxOI(_params.asset, _params.market, _params.size);

            // Transfer fee and margin to store
            valueConsumed = _params.margin + _params.fee;

            if (_params.asset == address(0)) {
                store.transferIn{value: valueConsumed + _params.orderDetail.executionFee }(_params.asset, msg.sender, valueConsumed + _params.orderDetail.executionFee);
                isSentNative = true;
            } else {
                store.transferIn(_params.asset, msg.sender, valueConsumed);
            }
        }

        if(_params.orderDetail.executionFee > 0 && !isSentNative){
            store.transferIn{value: _params.orderDetail.executionFee}(address(0), msg.sender, _params.orderDetail.executionFee);
        }

        // Add order to store and emit event
        _params.orderId = _add(_params);

        emit OrderCreated(
            _params.orderId,
            _params.user,
            _params.asset,
            _params.market,
            _params.margin,
            _params.size,
            _params.orderDetail.price,
            _params.fee,
            _params.isLong,
            _params.orderDetail.orderType,
            _params.orderDetail.isReduceOnly,
            _params.orderDetail.expiry,
            _params.orderDetail.cancelOrderId,
            msg.sender,
            _params.orderDetail.executionFee,
            _params.orderDetail.trailingStopPercentage
        );

        return (_params.orderId, valueConsumed);
    }

    /// @notice Cancels order
    /// @param _orderId Order to cancel
    function cancelOrder(uint32 _orderId) external ifNotPaused {
        Order memory order = orders[_orderId];
        require(order.size > 0, '!order');
        require(order.user == msg.sender, '!user');
        _cancelOrder(_orderId, 'by-user', msg.sender);
    }

    /// @notice Cancel several orders
    /// @param _orderIds Array of orderIds to cancel
    function cancelOrders(uint32[] calldata _orderIds) external ifNotPaused {
        for (uint32 i; i < _orderIds.length; i++) {
            Order memory order = orders[_orderIds[i]];
            if (order.size > 0 && order.user == msg.sender) {
                _cancelOrder(_orderIds[i], 'by-user', msg.sender);
            }
        }
    }

    /// @notice Cancels order
    /// @dev Only callable by Executor contract
    /// @param _orderId Order to cancel
    /// @param _reason Cancellation reason
    /// @param _executionFeeReceiver Address of execution fee receiver
    function cancelOrder(uint32 _orderId, string calldata _reason, address _executionFeeReceiver) external onlyExecutor {
        _cancelOrder(_orderId, _reason, _executionFeeReceiver);
    }

    /// @notice Cancels order
    /// @dev Internal function without access restriction
    /// @param _orderId Order to cancel
    /// @param _reason Cancellation reason
    /// @param _executionFeeReceiver Address of execution fee receiver
    function _cancelOrder(uint32 _orderId, string memory _reason, address _executionFeeReceiver) internal {
        Order memory order = orders[_orderId];
        if (order.size == 0) return;

        _remove(_orderId);
        bool isSentNative;

        if (!order.orderDetail.isReduceOnly) {
            isSentNative = order.asset == address(0) && order.user == _executionFeeReceiver;
            store.transferOut(order.asset, order.user, order.margin + order.fee + (isSentNative ? order.orderDetail.executionFee : 0));
        }

        if(order.orderDetail.executionFee > 0 && !isSentNative){
            store.transferOut(address(0), _executionFeeReceiver, order.orderDetail.executionFee);
        }

        emit OrderCancelled(_orderId, order.user, _executionFeeReceiver, _reason);
    }

    /// @notice Get order user 
    /// @dev if sender is whitelisted funding account and order is suitable, The user is being made incoming in the params 
    /// @param _params Order params
    /// @return user order user
    function _orderUser(Order memory _params) internal view returns (address) {
        if(whitelistedFundingAccount[msg.sender]){
            require(_params.user != address(0) && !_params.orderDetail.isReduceOnly && _params.orderDetail.orderType == 0,"funding-account-order-fail");
            return _params.user;        
        }
        return msg.sender;
    }

    /// @notice Adds order to storage
    /// @dev Only callable by PositionManager contract
    function add(Order memory _order) external onlyPositionManager returns (uint32) {
        return _add(_order);
    }

    /// @notice  Adds order to storage
    /// @dev Internal function
    function _add(Order memory _order) internal returns (uint32) {
        uint32 nextOrderId = ++oid;
        _order.orderId = nextOrderId;
        orders[nextOrderId] = _order;
        userOrderIds[_order.user].add(nextOrderId);
        if (_order.orderDetail.orderType == 0) {
            marketOrderIds.add(nextOrderId);
        } else {
            triggerOrderIds.add(nextOrderId);
        }
        emit AddOrder(nextOrderId, _order.orderDetail.orderType);

        return nextOrderId;
    }

    /// @notice Removes order from store
    /// @dev Only callable by PositionManager contract
    /// @param _orderId Order to remove
    function remove(uint32 _orderId) external onlyPositionManager {
        _remove(_orderId);
    }

    /// @notice  Removes order from store
    /// @dev Internal function
    /// @param _orderId Order to remove
    function _remove(uint32 _orderId) internal {
        Order memory order = orders[_orderId];
        if (order.size == 0) return;
        userOrderIds[order.user].remove(_orderId);
        marketOrderIds.remove(_orderId);
        triggerOrderIds.remove(_orderId);
        emit RemoveOrder(_orderId, order.orderDetail.orderType);
        delete orders[_orderId];

        
    }

    /// @notice Updates `cancelOrderId` of `orderId`, e.g. TP order cancels a SL order and vice versa
    /// @dev Internal function
    /// @param _orderId Order which cancels `cancelOrderId` on execution
    /// @param _cancelOrderId Order to cancel when `orderId` executes
    function _updateCancelOrderId(uint32 _orderId, uint32 _cancelOrderId) internal {
        OrderDetail storage orderDetail = orders[_orderId].orderDetail;
        orderDetail.cancelOrderId = _cancelOrderId;
    }

    /// @notice Returns a single order
    /// @param _orderId Order to get
    function get(uint32 _orderId) external view returns (Order memory) {
        return orders[_orderId];
    }

    /// @notice Returns many orders
    /// @param _orderIds Orders to get, e.g. [1, 2, 5]
    function getMany(uint32[] calldata _orderIds) external view returns (Order[] memory) {
        uint256 length = _orderIds.length;
        Order[] memory _orders = new Order[](length);

        for (uint256 i; i < length; i++) {
            _orders[i] = orders[_orderIds[i]];
        }

        return _orders;
    }

    /// @notice Returns market orders
    /// @param _length Amount of market orders to return
    function getMarketOrders(uint256 _length) external view returns (Order[] memory) {
        uint32 marketOrderlength = marketOrderIds.length().toUint32();
        if (_length > marketOrderlength) _length = marketOrderlength;

        Order[] memory _orders = new Order[](_length);

        for (uint256 i; i < _length; i++) {
            _orders[i] = orders[marketOrderIds.at(i).toUint32()];
        }

        return _orders;
    }

    /// @notice Returns trigger orders
    /// @param _length Amount of trigger orders to return
    /// @param _offset Offset to start
    function getTriggerOrders(uint256 _length, uint256 _offset) external view returns (Order[] memory) {
        uint32 triggerOrderlength = triggerOrderIds.length().toUint32();
        if (_length + _offset > triggerOrderlength) _length = triggerOrderlength - _offset; 

        Order[] memory _orders = new Order[](_length);

        for (uint256 i; i < _length ; i++) {
            _orders[i] = orders[triggerOrderIds.at(i+_offset).toUint32()];
        }

        return _orders;
    }

    /// @notice Returns orders of `user`    
    function getUserOrders(address _user) external view returns (Order[] memory) {
        uint32 length = userOrderIds[_user].length().toUint32();
        Order[] memory _orders = new Order[](length);

        for (uint256 i; i < length; i++) {
            _orders[i] = orders[userOrderIds[_user].at(i).toUint32()];
        }

        return _orders;
    }

    /// @notice Returns amount of market orders
    function getMarketOrderCount() external view returns (uint256) {
        return marketOrderIds.length();
    }

    /// @notice Returns amount of trigger orders
    function getTriggerOrderCount() external view returns (uint256) {
        return triggerOrderIds.length();
    }

    /// @notice Returns order amount of `user`
    function getUserOrderCount(address _user) external view returns (uint256) {
        return userOrderIds[_user].length();
    }

}

File 2 of 25 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 3 of 25 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 4 of 25 : 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 5 of 25 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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);
}

File 6 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 7 of 25 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/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;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    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));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 8 of 25 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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 9 of 25 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 10 of 25 : SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

File 11 of 25 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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. If 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)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 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) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 12 of 25 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 13 of 25 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 14 of 25 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 15 of 25 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 16 of 25 : FundingTracker.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

import './utils/AddressStorage.sol';
import './Store.sol';
import './PositionManager.sol';
import './utils/Governable.sol';

/**
 * @title  FundingTracker
 * @notice Funding rates are calculated hourly for each market and collateral
 *         asset based on the real-time open interest imbalance
 */
contract FundingTracker is Governable {

    // Constants
    uint256 public constant UNIT = 10 ** 18;

    // interval used to calculate accrued funding
    uint256 public fundingInterval = 1 hours;

    // asset => market => funding tracker (long) (short is opposite)
    mapping(address => mapping(bytes10 => int256)) private fundingTrackers;

    // asset => market => last time fundingTracker was updated. In seconds.
    mapping(address => mapping(bytes10 => uint256)) private lastUpdated;


    // Events
    event FundingUpdated(address indexed asset, bytes10 market, int256 fundingTracker, int256 fundingIncrement);
    event FundingIntervalUpdated(uint256 interval);
    event Link(address store, address positionManager, address executor);


    // Contracts
    AddressStorage public immutable addressStorage;
    Store public store;
    PositionManager public positionManager;
    address public executorAddress;

    /// @dev Only callable by PositionManager or Executor contracts
    modifier onlyPositionManagerAndExecutor() {
        require(msg.sender == address(positionManager) || msg.sender == executorAddress, "!unauthorized");
        _;
    }

    /// @dev Initializes addressStorage address
    constructor(AddressStorage _addressStorage) {
        addressStorage = _addressStorage;
    }

    /// @notice Set Funding Interval
    /// @dev Only callable by governance
    /// @param _interval Funding Interval
    function setFundingInterval(uint256 _interval) external onlyGov {
        require(_interval > 0, '!interval');
        fundingInterval = _interval;
        emit FundingIntervalUpdated(_interval);
    }

    /// @notice Initializes protocol contracts
    /// @dev Only callable by governance
    function link() external onlyGov {
        store = Store(addressStorage.getAddress('Store'));
        positionManager = PositionManager(addressStorage.getAddress('PositionManager'));
        executorAddress = addressStorage.getAddress('Executor');
        emit Link(
            address(store),
            address(positionManager),
            executorAddress
        );
    }

    /// @notice Returns last update timestamp of `asset` and `market`
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function getLastUpdated(address _asset, bytes10 _market) external view returns (uint256) {
        return lastUpdated[_asset][_market];
    }

    /// @notice Returns funding tracker of `asset` and `market`
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function getFundingTracker(address _asset, bytes10 _market) external view returns (int256) {
        return fundingTrackers[_asset][_market];
    }

    /// @notice Returns funding tracker of `asset` and `market` includes unrealized funding trackers
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function getNextFundingTracker(address _asset, bytes10 _market) external view returns (int256) {
        int256 fundingTracker = fundingTrackers[_asset][_market];    
        if(lastUpdated[_asset][_market]+fundingInterval <= block.timestamp){
            int256 fundingIncrement = getAccruedFunding(_asset, _market, 0); // in UNIT * bps
            if (fundingIncrement != 0){
                fundingTracker += fundingIncrement;
            }
        }
        return fundingTracker;
    }

    /// @notice Returns funding trackers of `assets` and `markets`
    /// @param _assets Array of asset addresses
    /// @param _markets Array of market bytes10s
    function getFundingTrackers(
        address[] calldata _assets,
        bytes10[] calldata _markets
    ) external view returns (int256[] memory fts) {
        uint256 assetLength = _assets.length;
        uint256 marketLength = _markets.length;
        fts = new int256[](assetLength * marketLength);
        uint index;
        for (uint256 i; i < assetLength; i++) {
             for (uint256 j; j < marketLength; j++) {
                index = (i * marketLength) + j;
                fts[index] = fundingTrackers[_assets[i]][_markets[j]];
            }
        }
        return fts;
    }

    /// @notice Returns funding trackers of `assets` and `markets` includes unrealized funding trackers
    /// @param _assets Array of asset addresses
    /// @param _markets Array of market bytes10s
    function getNextFundingTrackers(
        address[] calldata _assets,
        bytes10[] calldata _markets
    ) external view returns (int256[] memory fts) {
        uint256 assetLength = _assets.length;
        uint256 marketLength = _markets.length;
        fts = new int256[](assetLength * marketLength);
        uint index;
        for (uint256 i; i < assetLength; i++) {
             for (uint256 j; j < marketLength; j++) {
                index = (i * marketLength) + j;
                fts[index] = fundingTrackers[_assets[i]][_markets[j]];
                if(lastUpdated[_assets[i]][_markets[j]]+fundingInterval <= block.timestamp){
                    int256 fundingIncrement = getAccruedFunding(_assets[i], _markets[j], 0); // in UNIT * bps
                    if (fundingIncrement != 0){
                        fts[index] += fundingIncrement;
                    }
                }
            }
        }
        return fts;
    }


    /// @notice Updates funding tracker of `market` and `asset`
    /// @dev Only callable by position manager or executor contracts
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function updateFundingTracker(address _asset, bytes10 _market) external onlyPositionManagerAndExecutor {
        uint256 _lastUpdated = lastUpdated[_asset][_market];
        uint256 _now = block.timestamp;

        // condition is true only on the very first execution
        if (_lastUpdated == 0) {
            lastUpdated[_asset][_market] = _now;
            return;
        }

        // returns if block.timestamp - lastUpdated is less than funding interval
        if (_lastUpdated + fundingInterval > _now) return;

        // positive funding increment indicates that shorts pay longs, negative that longs pay shorts
        int256 fundingIncrement = getAccruedFunding(_asset, _market, 0); // in UNIT * bps

        lastUpdated[_asset][_market] = _now;

        // return if funding increment is zero
        if (fundingIncrement == 0) return;

        fundingTrackers[_asset][_market] += fundingIncrement;

        emit FundingUpdated(_asset, _market, fundingTrackers[_asset][_market], fundingIncrement);
    }

    /// @notice Returns accrued funding of `market` and `asset`
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD"
    /// @param _intervals intervals , if 0, it is calculated from the last updated point until now,if fundingInterval is 3600,then 1 for 1h ,24 for 1 day 
    function getAccruedFunding(address _asset, bytes10 _market, uint256 _intervals) public view returns (int256) {
        if (_intervals == 0) {
            _intervals = (block.timestamp - lastUpdated[_asset][_market]) / fundingInterval;
        }

        if (_intervals == 0) return 0;

        uint256 OILong = positionManager.getOILong(_asset, _market);
        uint256 OIShort = positionManager.getOIShort(_asset, _market);

        if (OIShort == 0 && OILong == 0) return 0;

        uint256 OIDiff = OIShort > OILong ? OIShort - OILong : OILong - OIShort;

        Store.Market memory marketInfo = store.getMarket(_market);
        uint256 yearlyFundingFactor = marketInfo.fundingFactor;


        uint256 accruedFunding = (UNIT * yearlyFundingFactor * OIDiff * _intervals) / ((365 days / fundingInterval) * (OILong + OIShort)); // in UNIT * bps

        if (OILong > OIShort) {
            // Longs pay shorts. Increase funding tracker.
            return int256(accruedFunding);
        } else {
            // Shorts pay longs. Decrease funding tracker.
            return -1 * int256(accruedFunding);
        }
    }
}

File 17 of 25 : IReferralStorage.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

interface IReferralStorage {
    function codeOwners(bytes32 _code) external view returns (address);
    function traderReferralCodes(address _account) external view returns (bytes32);
    function referrerDiscountShares(address _account) external view returns (uint256);
    function referrerTiers(address _account) external view returns (uint256);
    function getTraderReferralInfo(address _account) external view returns (bytes32, address);
    function setTraderReferralCode(address _account, bytes32 _code) external;
    function setTier(uint256 _tierId, uint256 _totalRebate, uint256 _discountShare) external;
    function setReferrerTier(address _referrer, uint256 _tierId) external;
    function govSetCodeOwner(bytes32 _code, address _newAccount) external;
}

File 18 of 25 : PositionManager.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/utils/math/SafeCast.sol';


import './utils/AddressStorage.sol';
import './utils/TradingValidator.sol';


import './FundingTracker.sol';
import './Store.sol';
import './OrderBook.sol';
import './utils/interfaces/IStore.sol';


import './utils/interfaces/IReferencePriceFeed.sol';
import './interfaces/IReferralStorage.sol';
import './utils/Governable.sol';

/**
 * @title  PositionManager
 * @notice Implementation of position related logic, i.e. increase positions,
 *         decrease positions, close positions, add/remove margin
 */
contract PositionManager is Governable {

    // Constants
    uint256 public constant UNIT = 10 ** 18;
    uint256 public constant BPS_DIVIDER = 10000;

        // Libraries
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using SafeCast for uint256;

    // Position struct
    struct Position {
        address user; // User that submitted the position
        bytes10 market; // Market this position was submitted on
        bool isLong; // Whether the position is long or short
        address asset; // Asset address, e.g. address(0) for ETH
        uint96 size; // The position's size (margin * leverage)
        uint32 timestamp; // Time at which the position was created
        uint96 margin; // Collateral tied to this position. In wei
        uint96 price; // The position's average execution price
        int256 fundingTracker; // Market funding rate tracker
    }

    struct OpenInterest {
        uint128 long; 
        uint128 short; 
    }

    // Constants
    uint256 public constant MAX_KEEPER_FEE_SHARE = 2000; // 20%
    uint256 public constant MAX_FEE = 500; // 5%
    uint256 public constant MAX_MIN_POSITION_HOLD_TIME = 1800; // 30 min.
    
    // State variables
    uint256 public removeMarginBuffer = 1000;
    uint256 public keeperFeeShare = 500;
    uint256 public trailingStopFee = 100; // 1%
    uint256 public minPositionHoldTime;

    // Mappings
    mapping(address => OpenInterest) private assetOI; // open interest. asset => OpenInterest

    mapping(address => mapping(bytes10 => OpenInterest)) private OI; // open interest. market => asset => OpenInterest

    mapping(bytes32 => Position) private positions; // key = asset,user,market
    EnumerableSet.Bytes32Set private positionKeys; // [position keys..]
    mapping(address => EnumerableSet.Bytes32Set) private positionKeysForUser; // user => [position keys..]
    // user => market => last position increase
    mapping(address => mapping(bytes10 => uint256)) private lastIncreased;

    // Contracts
    AddressStorage public immutable addressStorage;
    OrderBook public orderBook;
    TradingValidator public tradingValidator;
    IReferralStorage public referralStorage;
    FundingTracker public fundingTracker;
    Store public store;
    IReferencePriceFeed public referencePriceFeed;
    address public executorAddress;

    // Events
    event PositionIncreased(
        uint256 indexed orderId,
        address indexed user,
        address indexed asset,
        bytes10 market,
        bool isLong,
        uint256 size,
        uint256 margin,
        uint256 price,
        uint256 positionMargin,
        uint256 positionSize,
        uint256 positionPrice,
        int256 fundingTracker,
        uint256 fee
    );

    event PositionDecreased(
        uint256 indexed orderId,
        address indexed user,
        address indexed asset,
        bytes10 market,
        bool isLong,
        uint256 size,
        uint256 margin,
        uint256 price,
        uint256 positionMargin,
        uint256 positionSize,
        uint256 positionPrice,
        int256 fundingTracker,
        uint256 fee,
        int256 pnl,
        int256 pnlUsd,
        int256 fundingFee,
        bool isTrailingStop
    );

    event MarginIncreased(
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint256 marginDiff,
        uint256 positionMargin
    );

    event MarginDecreased(
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint256 marginDiff,
        uint256 positionMargin
    );

    event FeePaid(
        uint256 indexed orderId,
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint256 fee,
        uint256 poolFee,
        uint256 treasuryFee,
        uint256 keeperFee,
        uint256 executionFee,
        bool isLiquidation
    );

    event IncreasePositionReferral(
        address indexed account,
        address indexed asset,
        uint256 sizeDelta,
        uint256 fee,
        bytes32 referralCode,
        address referrer
    );

    event DecreasePositionReferral(
        address indexed account,
        address indexed asset,
        uint256 sizeDelta,
        uint256 fee,
        bytes32 referralCode,
        address referrer
    );

    event MinPositionHoldTimeUpdated(uint256 minPositionHoldTime);
    event RemoveMarginBufferUpdated(uint256 bps);
    event KeeperFeeShareUpdated(uint256 keeperFeeShare);
    event TrailingStopFeeUpdated(uint256 trailingStopFee);
    event Link(address orderBook, address tradingValidator, address fundingTracker, address store, address referencePriceFeed, address referralStorage, address executorAddress);
    event RemovePosition(address indexed user, address indexed asset, bytes10 indexed market);
    event IncrementOI(address indexed asset, bytes10 indexed market, uint96 amount, bool isLong);
    event DecrementOI(address indexed asset, bytes10 indexed market, uint96 amount, bool isLong);

    /// @dev Reverts if order processing is paused
    modifier ifNotPaused() {
        require(!orderBook.areNewOrdersPaused(), '!paused');
        _;
    }

    /// @dev Only callable by Executor contract
    modifier onlyExecutor() {
        require(msg.sender == executorAddress, "!unauthorized");
        _;
    }

    /// @dev Initializes addressStorage address
    constructor(AddressStorage _addressStorage)  {
        addressStorage = _addressStorage;
    }

    /// @notice Initializes protocol contracts
    /// @dev Only callable by governance
    function link() external onlyGov {
        orderBook = OrderBook(addressStorage.getAddress('OrderBook'));
        tradingValidator = TradingValidator(addressStorage.getAddress('TradingValidator'));
        fundingTracker = FundingTracker(addressStorage.getAddress('FundingTracker'));
        store = Store(addressStorage.getAddress('Store'));
        referencePriceFeed = IReferencePriceFeed(addressStorage.getAddress('ReferencePriceFeed'));
        referralStorage = IReferralStorage(addressStorage.getAddress('ReferralStorage'));
        executorAddress = addressStorage.getAddress('Executor');
        emit Link(
            address(orderBook), 
            address(tradingValidator), 
            address(fundingTracker), 
            address(store), 
            address(referencePriceFeed), 
            address(referralStorage), 
            executorAddress
        );
    }
    
    /// @notice Set minimum position hold time
    /// @dev Only callable by governance
    /// @param _minPositionHoldTime  minimum position hold time in seconds
    function setMinPositionHoldTime(uint256 _minPositionHoldTime) external onlyGov {
        require(_minPositionHoldTime <= MAX_MIN_POSITION_HOLD_TIME, '!min-position-hold-time');
        minPositionHoldTime = _minPositionHoldTime;
        emit MinPositionHoldTimeUpdated(_minPositionHoldTime);
    }

    /// @notice Updates `removeMarginBuffer`
    /// @dev Only callable by governance
    /// @param _bps new `removeMarginBuffer` in bps
    function setRemoveMarginBuffer(uint256 _bps) external onlyGov {
        require(_bps < BPS_DIVIDER, '!bps');
        removeMarginBuffer = _bps;
        emit RemoveMarginBufferUpdated(_bps);
    }

    /// @notice Sets keeper fee share
    /// @dev Only callable by governance
    /// @param _keeperFeeShare new `keeperFeeShare` in bps
    function setKeeperFeeShare(uint256 _keeperFeeShare) external onlyGov {
        require(_keeperFeeShare <= MAX_KEEPER_FEE_SHARE, '!keeper-fee-share');
        keeperFeeShare = _keeperFeeShare;
        emit KeeperFeeShareUpdated(_keeperFeeShare);
    }

    /// @notice Sets trailing stop fee
    /// @dev Only callable by governance
    /// @dev Only applies if position closed by trailing stop order keeper
    /// @param _trailingStopFee new `trailingStopFee` in bps
    function setTrailingStopFee(uint256 _trailingStopFee) external onlyGov {
        require(_trailingStopFee <= MAX_FEE, '!trailing-stop-fee');
        trailingStopFee = _trailingStopFee;
        emit TrailingStopFeeUpdated(_trailingStopFee);
    }

    /// @notice Opens a new position or increases existing one
    /// @dev Only callable by Executor contract
    function increasePosition(uint32 _orderId, uint256 _price, address _keeper) external onlyExecutor {
        _increasePosition(_orderId,_price,_keeper);
    }

    /// @notice Opens a new position or increases existing one
    /// @dev Internal function invoked by {increasePosition,_decreasePosition}
    function _increasePosition(uint32 _orderId, uint256 _price, address _keeper) internal {
        OrderBook.Order memory order = orderBook.get(_orderId);

        // Check if maximum open interest is reached
        tradingValidator.checkMaxOI(order.asset, order.market, order.size);
        // FundingTracker update must be before increment
        fundingTracker.updateFundingTracker(order.asset, order.market);
        _incrementOI(order.asset, order.market, order.size, order.isLong);


        Position memory position = getPosition(order.user, order.asset, order.market);
        uint96 averagePrice = ((uint256(position.size) * position.price + order.size * _price) / (position.size + order.size)).toUint96();

        // Populate position fields if new position
        if (position.size == 0) {
            position.user = order.user;
            position.asset = order.asset;
            position.market = order.market;
            position.timestamp = block.timestamp.toUint32();
            position.isLong = order.isLong;
            position.fundingTracker = fundingTracker.getFundingTracker(order.asset, order.market);
        }

        // Add or update position
        position.size += order.size;
        position.margin += order.margin;
        position.price = averagePrice;

        _addOrUpdate(position);

        // Remove order
        orderBook.remove(_orderId);

        // Credit fee to _keeper, pool, stakers, treasury
        _creditFee(_orderId, order.user, order.asset, order.market, order.fee, order.orderDetail.executionFee, false, _keeper);

        lastIncreased[order.user][order.market] = block.timestamp;

        emit PositionIncreased(
            _orderId,
            order.user,
            order.asset,
            order.market,
            order.isLong,
            order.size,
            order.margin,
            _price,
            position.margin,
            position.size,
            position.price,
            position.fundingTracker,
            order.fee
        );

        _emitIncreasePositionReferral(order.user, order.asset, order.size, order.fee);
    }

    /// @notice Decreases or closes an existing position
    /// @dev Only callable by Executor contract
    function decreasePosition(uint32 _orderId, uint256 price, bool _isTrailingStop, address _keeper) external onlyExecutor {
        _decreasePosition(_orderId, price, _isTrailingStop, _keeper);
    }    

    /// @notice Decreases or closes an existing position
    /// @dev Internal function invoked by {decreasePosition}
    function _decreasePosition(uint32 _orderId, uint256 price, bool _isTrailingStop, address _keeper) internal {
        OrderBook.Order memory order = orderBook.get(_orderId);
        Position memory position = getPosition(order.user, order.asset, order.market);

        // Check last increased
        require(lastIncreased[order.user][order.market] < block.timestamp - minPositionHoldTime, "!min-hold-time");

        // If position size is less than order size, not all will be executed
        uint256 executedOrderSize = position.size > order.size ? order.size : position.size;
        uint256 remainingOrderSize = order.size - executedOrderSize;

        uint256 remainingOrderMargin;
        uint256 amountToReturnToUser;

        if (!order.orderDetail.isReduceOnly) {
            // User submitted order.margin when sending the order. Refund the portion of order.margin
            // that executes against the position
            uint256 executedOrderMargin = (order.margin * executedOrderSize) / order.size;
            amountToReturnToUser += executedOrderMargin;
            remainingOrderMargin = order.margin - executedOrderMargin;
        }
        
        if(position.size > executedOrderSize){
            IStore.Asset memory assetInfo = store.getAsset(order.asset);
            require(position.size - executedOrderSize >= assetInfo.minSize,"!min-remaining-size");
        }

        // Calculate fee based on executed order size

        uint256 fee = ((order.fee + (_isTrailingStop ? (executedOrderSize * trailingStopFee) / BPS_DIVIDER : 0) ) * executedOrderSize) / order.size;

        _creditFee(_orderId, order.user, order.asset, order.market, fee, order.orderDetail.executionFee, false, _keeper);

        // If an order is reduce-only, fee is taken from the position's margin.
        uint256 feeToPay = order.orderDetail.isReduceOnly ? fee : 0;

        // FundingTracker update must be before decrementOI
        fundingTracker.updateFundingTracker(order.asset, order.market);

        // Get PNL of position
        (int256 pnl, int256 fundingFee) = getPnL(
            order.asset,
            order.market,
            position.isLong,
            price,
            position.price,
            executedOrderSize,
            position.fundingTracker
        );

        uint256 executedPositionMargin = (position.margin * executedOrderSize) / position.size;

        // If PNL is less than position margin, close position, else update position
        if (pnl <= -1 * int256(uint256(position.margin))) {
            pnl = -1 * int256(uint256(position.margin));
            executedPositionMargin = position.margin;
            executedOrderSize = position.size;
            position.size = 0;
        } else {
            position.margin -= executedPositionMargin.toUint96();
            position.size -= executedOrderSize.toUint96();
            position.fundingTracker = fundingTracker.getFundingTracker(order.asset, order.market);
        }

        _decrementOI(order.asset, order.market, executedOrderSize.toUint96(), position.isLong);

        // Check for maximum pool drawdown
        tradingValidator.checkPoolDrawdown(order.asset, pnl);

        // Credit trader loss or debit trader profit based on pnl
        if (pnl < 0) {
            uint256 absPnl = uint256(-1 * pnl);
            store.creditTraderLoss(order.user, order.asset, order.market, absPnl);

            uint256 totalPnl = absPnl + feeToPay;

            // If an order is reduce-only, fee is taken from the position's margin as the order's margin is zero.
            if (totalPnl < executedPositionMargin) {
                amountToReturnToUser += executedPositionMargin - totalPnl;
            }
        } else {
            store.debitTraderProfit(order.user, order.asset, order.market, uint256(pnl));

            // If an order is reduce-only, fee is taken from the position's margin as the order's margin is zero.
            amountToReturnToUser += executedPositionMargin - feeToPay;
        }

        if (position.size == 0) {
            // Remove position if size == 0
            _remove(order.user, order.asset, order.market);
        } else {
            _addOrUpdate(position);
        }

        // Remove order and transfer funds out
        orderBook.remove(_orderId);
        store.transferOut(order.asset, order.user, amountToReturnToUser);

        emit PositionDecreased(
            _orderId,
            order.user,
            order.asset,
            order.market,
            order.isLong,
            executedOrderSize,
            executedPositionMargin,
            price,
            position.margin,
            position.size,
            position.price,
            position.fundingTracker,
            feeToPay,
            pnl,
            _getUsdAmount(order.asset, pnl),
            fundingFee,
            _isTrailingStop
        );

        // Open position in opposite direction if size remains
        if (!order.orderDetail.isReduceOnly && remainingOrderSize > 0) {

            OrderBook.OrderDetail memory orderDetail = OrderBook.OrderDetail({
                    orderType: 0,
                    isReduceOnly: false,
                    price: 0,
                    expiry: 0,
                    cancelOrderId: 0,
                    executionFee: 0,
                    trailingStopPercentage : 0
                });


            OrderBook.Order memory nextOrder = OrderBook.Order({

                user: order.user,
                margin: remainingOrderMargin.toUint96(),
                asset: order.asset,
                market: order.market,
                isLong: order.isLong,
                size: remainingOrderSize.toUint96(),
                fee: ((order.fee * remainingOrderSize) / order.size).toUint96(),
                timestamp: block.timestamp.toUint32(),
                orderId : 0,
                orderDetail:orderDetail
            });

            uint32 nextOrderId = orderBook.add(nextOrder);

            _increasePosition(nextOrderId, price, _keeper);
        }

        _emitDecreasePositionReferral(order.user, order.asset, executedOrderSize, fee);
    }

    /// @notice Add margin to a position to decrease its leverage and push away its liquidation price
    function addMargin(address _asset, bytes10 _market, uint96 _margin) external payable ifNotPaused {
        address user = msg.sender;

        Position memory position = getPosition(user, _asset, _market);
        require(position.size > 0, '!position');

        // Transfer additional margin in
        if (_asset == address(0)) {
            _margin = msg.value.toUint96();
            store.transferIn{value: _margin}(_asset, user, _margin);
        } else {
            store.transferIn(_asset, user, _margin);
        }

        require(_margin > 0, '!margin');

        // update position margin
        position.margin += _margin;

        // Check if leverage is above minimum leverage
        uint256 leverage = (UNIT * position.size) / position.margin;
        require(leverage >= UNIT, '!min-leverage');

        // update position
        _addOrUpdate(position);

        emit MarginIncreased(user, _asset, _market, _margin, position.margin);
    }

    /// @notice Remove margin from a position to increase its leverage
    /// @dev Margin removal is only available on markets supported by referencePrice
    function removeMargin(address _asset, bytes10 _market, uint256 _margin) external ifNotPaused {
        address user = msg.sender;

        IStore.Market memory marketInfo = store.getMarket(_market);

        Position memory position = getPosition(user, _asset, _market);
        require(position.size > 0, '!position');
        require(position.margin > _margin, '!margin');

        uint256 remainingMargin = position.margin - _margin;

        // Leverage
        uint256 leverageAfterRemoval = (UNIT * position.size) / remainingMargin;
        require(leverageAfterRemoval <= marketInfo.maxLeverage * UNIT, '!max-leverage');

        // This is not available for markets without referencePrice
        uint256 price = referencePriceFeed.getPrice(marketInfo.referencePriceFeed);
        require(price > 0, '!price');

        (int256 upl, ) = getPnL(
            _asset,
            _market,
            position.isLong,
            price,
            position.price,
            position.size,
            position.fundingTracker
        );

        if (upl < 0) {
            uint256 absUpl = uint256(-1 * upl);
            require(
                absUpl < (remainingMargin * (BPS_DIVIDER - removeMarginBuffer)) / BPS_DIVIDER,
                '!upl'
            );
        }

        // Update position and transfer margin out
        position.margin = remainingMargin.toUint96();
        _addOrUpdate(position);

        store.transferOut(_asset, user, _margin);

        emit MarginDecreased(user, _asset, _market, _margin, position.margin);
    }

    /// @notice Credit fee to Keeper, Pool, and Treasury
    /// @dev Only callable by Executor contract
    function creditFee(
        uint256 _orderId,
        address _user,
        address _asset,
        bytes10 _market,
        uint256 _fee,
        uint256 _executionFee,
        bool _isLiquidation,
        address _keeper
    ) external onlyExecutor {
        _creditFee(_orderId, _user, _asset, _market, _fee, _executionFee, _isLiquidation, _keeper);
    }    

    /// @notice Credit fee to Keeper, Pool, and Treasury
    /// @dev Internal function invoked by {creditFee,_increasePosition,_decreasePosition}
    /// @param _orderId order id
    /// @param _user user address
    /// @param _asset Base asset of position
    /// @param _market Market position was submitted on
    /// @param _fee fee paid
    /// @param _executionFee execution fee with native token
    /// @param _isLiquidation whether position is liquidated
    /// @param _keeper keeper address
    function _creditFee(
        uint256 _orderId,
        address _user,
        address _asset,
        bytes10 _market,
        uint256 _fee,
        uint256 _executionFee,
        bool _isLiquidation,
        address _keeper
    ) internal {
        if (_fee == 0) return;

        // multiply _fee by UNIT (10^18) to increase position
        _fee = _fee * UNIT;

        uint256 keeperFee;
        uint256 netFee = _fee;

        if (keeperFeeShare > 0) {
            keeperFee = (_fee * keeperFeeShare) / BPS_DIVIDER;
            netFee = _fee - keeperFee;
        }

        // Calculate fees
        uint256 feeToPool = (netFee * store.feeShare()) / BPS_DIVIDER;
        uint256 feeToTreasury = netFee - feeToPool;

        // Increment balances, transfer fees out
        // Divide _fee by UNIT to get original _fee value back
        store.incrementBalance(_asset, feeToPool / UNIT);

        store.addFees(_asset, feeToTreasury / UNIT);
        if(keeperFee > 0){
            store.transferOut(_asset, _keeper, keeperFee / UNIT);
        }
        if(_executionFee > 0){
            store.transferOut(address(0), _keeper, _executionFee);
        }

        emit FeePaid(
            _orderId,
            _user,
            _asset,
            _market,
            _fee / UNIT, // paid by user
            feeToPool / UNIT,
            feeToTreasury / UNIT,
            keeperFee / UNIT,
            _executionFee,
            _isLiquidation
        );
    }

    /// @notice Get pnl of a position
    /// @param _asset Base asset of position
    /// @param _market Market position was submitted on
    /// @param _isLong Whether position is long or short
    /// @param _price Current price of market
    /// @param _positionPrice Average execution price of position
    /// @param _size Positions size (margin * leverage) in wei
    /// @param _fundingTracker Market funding rate tracker
    /// @return pnl Profit and loss of position
    /// @return fundingFee Funding fee of position
    function getPnL(
        address _asset,
        bytes10 _market,
        bool _isLong,
        uint256 _price,
        uint256 _positionPrice,
        uint256 _size,
        int256 _fundingTracker
    ) public view returns (int256 pnl, int256 fundingFee) {
        if (_price == 0 || _positionPrice == 0 || _size == 0) return (0, 0);

        if (_isLong) {
            pnl = (int256(_size) * (int256(_price) - int256(_positionPrice))) / int256(_positionPrice);
        } else {
            pnl = (int256(_size) * (int256(_positionPrice) - int256(_price))) / int256(_positionPrice);
        }

        int256 currentFundingTracker = fundingTracker.getNextFundingTracker(_asset, _market);

        fundingFee = (int256(_size) * (currentFundingTracker - _fundingTracker)) / (int256(BPS_DIVIDER) * int256(UNIT)); // funding tracker is in UNIT * bps

        if (_isLong) {
            pnl -= fundingFee; // positive = longs pay, negative = longs receive
        } else {
            pnl += fundingFee; // positive = shorts receive, negative = shorts pay
        }

        return (pnl, fundingFee);
    }

    /// @dev Returns USD value of `_amount` of `_asset`
    /// @dev Used for PositionDecreased event
    function _getUsdAmount(address _asset, int256 _amount) internal view returns (int256) {
        IStore.Asset memory assetInfo = store.getAsset(_asset);
        uint256 referencePrice = referencePriceFeed.getPrice(assetInfo.referencePriceFeed);

        // _amount is in the _asset's decimals, convert to 18. Price is 18 decimals
        return (_amount * int256(referencePrice)) / int256(10 ** assetInfo.decimals);
    }

    /// @dev emit increase position referral event
    function _emitIncreasePositionReferral(address _account, address _asset, uint256 _sizeDelta, uint256 _fee) internal {
        if (address(referralStorage) == address(0)) { return; }


        (bytes32 referralCode, address referrer) = referralStorage.getTraderReferralInfo(_account);
        if (referralCode == bytes32(0)) { return; }

        emit IncreasePositionReferral(
            _account,
            _asset,
            _sizeDelta,
            _fee,
            referralCode,
            referrer
        );
    }

    /// @dev emit decrease position referral event
    function _emitDecreasePositionReferral(address _account, address _asset, uint256 _sizeDelta, uint256 _fee) internal {
        if (address(referralStorage) == address(0)) { return; }


        (bytes32 referralCode, address referrer) = referralStorage.getTraderReferralInfo(_account);
        if (referralCode == bytes32(0)) { return; }

        emit DecreasePositionReferral(
            _account,
            _asset,
            _sizeDelta,
            _fee,
            referralCode,
            referrer
        );
    }

    /// @notice Adds new position or updates exisiting one
    /// @dev Only callable by other protocol contracts
    /// @param _position Position to add/update
    function _addOrUpdate(Position memory _position) internal  {
        bytes32 key = _getPositionKey(_position.user, _position.asset, _position.market);
        positions[key] = _position;
        positionKeysForUser[_position.user].add(key);
        positionKeys.add(key);
    }

    /// @notice Removes position
    /// @dev Only callable by Executor contract
    /// @param _user User address
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function remove(address _user, address _asset, bytes10 _market) external onlyExecutor {
        _remove(_user, _asset, _market);
    }

    /// @notice Removes position
    /// @dev Internal function invoked by {remove, _decreasePosition}
    function _remove(address _user, address _asset, bytes10 _market) internal {
        bytes32 key = _getPositionKey(_user, _asset, _market);
        positionKeysForUser[_user].remove(key);
        positionKeys.remove(key);
        delete positions[key];
        emit RemovePosition(_user,_asset,_market);
    }

    /// @notice Increments open interest
    /// @dev Invoked by increasePosition
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD"
    /// @param _amount position size amount
    /// @param _isLong Whether position is long or short 
    function _incrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) internal {
        if (_isLong) {
            OI[_asset][_market].long += _amount;
            assetOI[_asset].long += _amount;
        } else {
            OI[_asset][_market].short += _amount;
            assetOI[_asset].short += _amount;
        }
        emit IncrementOI(_asset, _market, _amount, _isLong);
    }

    /// @notice Decrements open interest
    /// @dev Only callable by Executor contract
    /// @dev Invoked whenever a position is closed or decreased
    function decrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) external onlyExecutor {
        _decrementOI(_asset, _market, _amount, _isLong);
    }

    /// @notice Decrements open interest
    /// @dev internal function
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD"
    /// @param _amount position size amount
    /// @param _isLong Whether position is long or short 
    function _decrementOI(address _asset, bytes10 _market, uint96 _amount, bool _isLong) internal {
        
        if (_isLong) {
            uint128 long = OI[_asset][_market].long;
            OI[_asset][_market].long = long <= _amount ? 0 : long - _amount;
            uint128 assetLong = assetOI[_asset].long;
            assetOI[_asset].long = assetLong <= _amount ? 0 : assetLong - _amount;
        } else {
            uint128 short = OI[_asset][_market].short;
            OI[_asset][_market].short = short <= _amount ? 0 : short - _amount;
            uint128 assetShort = assetOI[_asset].short;
            assetOI[_asset].short = assetShort <= _amount ? 0 : assetShort - _amount;
        }
        emit DecrementOI(_asset, _market, _amount, _isLong);
    }

    /// @notice Returns open interest of `_asset` and `_market`
    function getOI(address _asset,bytes10 _market) external view returns (uint256) {
        OpenInterest memory oi = OI[_asset][_market];
        return oi.long + oi.short;
    }

    /// @notice Returns open interest of `_asset` 
    function getAssetOI(address _asset) external view returns (uint256) {
        OpenInterest memory oi = assetOI[_asset];
        return oi.long + oi.short;
    }

    
    /// @notice Returns open interest of long positions of `_asset`
    function getAssetOILong(address _asset) external view returns (uint256) {
        return assetOI[_asset].long;
    }

    /// @notice Returns open interest of short positions of `_asset`
    function getAssetOIShort(address _asset) external view returns (uint256) {
        return assetOI[_asset].short;
    }

    /// @notice Returns open interest of long positions of `_asset` and `_market`
    function getOILong(address _asset, bytes10 _market) external view returns (uint256) {
        return OI[_asset][_market].long;
    }

    /// @notice Returns open interest of short positions of `_asset` and `_market`
    function getOIShort(address _asset, bytes10 _market) external view returns (uint256) {
        return OI[_asset][_market].short;
    }


    /// @notice Returns position of `_user`
    /// @param _asset Base asset of position
    /// @param _market Market this position was submitted on
    function getPosition(address _user, address _asset, bytes10 _market) public view returns (Position memory) {
        bytes32 key = _getPositionKey(_user, _asset, _market);
        return positions[key];
    }

    /// @notice Returns positions of `_users`
    /// @param _assets Base assets of positions
    /// @param _markets Markets of positions
    function getPositions(
        address[] calldata _users,
        address[] calldata _assets,
        bytes10[] calldata _markets
    ) external view returns (Position[] memory) {
        uint256 length = _users.length;
        Position[] memory _positions = new Position[](length);

        for (uint256 i; i < length; i++) {
            _positions[i] = getPosition(_users[i], _assets[i], _markets[i]);
        }

        return _positions;
    }

    /// @notice Returns positions
    /// @param _keys Position keys
    function getPositions(bytes32[] calldata _keys) external view returns (Position[] memory) {
        uint256 length = _keys.length;
        Position[] memory positionList = new Position[](length);

        for (uint256 i; i < length; i++) {
            positionList[i] = positions[_keys[i]];
        }

        return positionList;
    }

    /// @notice Returns number of positions
    function getPositionCount() external view returns (uint256) {
        return positionKeys.length();
    }

    /// @notice Returns `_length` amount of positions starting from `_offset`
    function getPositions(uint256 _length, uint256 _offset) external view returns (Position[] memory) {
        uint256 keyslength = positionKeys.length();
        if (_length + _offset > keyslength) _length = keyslength - _offset;
        Position[] memory _positions = new Position[](_length);

        for (uint256 i; i < _length ; i++) {
            _positions[i] = positions[positionKeys.at(i+_offset)];
        }

        return _positions;
    }

    /// @notice Returns all positions of `_user`
    function getUserPositions(address _user) external view returns (Position[] memory) {
        uint256 length = positionKeysForUser[_user].length();
        Position[] memory _positions = new Position[](length);

        for (uint256 i; i < length; i++) {
            _positions[i] = positions[positionKeysForUser[_user].at(i)];
        }

        return _positions;
    }

    /// @dev Returns position key by hashing (user, asset, market)
    function _getPositionKey(address _user, address _asset, bytes10 _market) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_user, _asset, _market));
    }

}

File 19 of 25 : Store.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import '@openzeppelin/contracts/utils/math/SafeCast.sol';
import './utils/AddressStorage.sol';
import './utils/Governable.sol';
import './utils/interfaces/IStore.sol';
import './PositionManager.sol';

/**
 * @title  Store
 * @notice Users can deposit supported assets to back trader profits and receive
 *         a share of trader losses. Each asset pool is siloed, e.g. the ETH
 *         pool is independent from the USDC pool.
 *         Persistent storage of supported assets
 *         Persistent storage of supported markets
 *         Storage of protocol funds
 */
contract Store is Governable, ReentrancyGuard, IStore {
    // Libraries
    using SafeERC20 for IERC20;
    using Address for address payable;
    using EnumerableSet for EnumerableSet.UintSet;
    using SafeCast for uint256;

    // Constants
    uint256 public constant BPS_DIVIDER = 10000;
    uint256 public constant MAX_FEE = 1000; // 10%
    uint256 public constant MAX_DEVIATION = 1000; // 10%
    uint256 public constant MAX_LIQTHRESHOLD = 9800; // 98%
    uint256 public constant MAX_MIN_ORDER_AGE = 30; //seconds
    uint256 public constant MIN_PYTH_MAX_AGE = 3; //seconds
    uint256 public constant MAX_BUFFER_PAYOUT_PERIOD = 7 days; 


    // Liquidity struct
    enum LiquidityType {
        DEPOSIT,
        WITHDRAW
    }    

    struct LiquidityOrder { 
        address asset; // Asset address, e.g. address(0) for ETH
        uint96 amount; // liquidity order amount
        address user; // user that submitted the order
        uint32 liquidityOrderId;  // incremental order id 
        LiquidityType orderType; // 0- Deposit, 1- Withdraw
        uint32 timestamp; // block.timestamp at which the order was submitted
        uint96 minAmountMinusTax; // realised amount minus tax must be greater than this value
        uint64 executionFee; //Fee paid with native token for keeper's execution
    }

    // State variables
    uint256 public feeShare = 500;
    uint256 public bufferPayoutPeriod = 7 days;
    bool public isPublicDeposit;
    uint256 public maxLiquidityOrderTTL = 5 minutes;
    uint32 public liquidityOid; // incremental liquidity order id
    mapping(uint32 => LiquidityOrder) private liquidityOrders; // order id => Order
    mapping(address => EnumerableSet.UintSet) private userLiquidityOrderIds; // user => [order ids..]
    EnumerableSet.UintSet private liquidityOrderIds; // [order ids..]
    uint64 public orderExecutionFee;  //  fee with native token required for the execution of the order by keepers

    // Asset list
    address[] public assetList;
    mapping(address => Asset) private assets;

    // Market list
    bytes10[] public marketList; 
    mapping(bytes10 => Market) private markets;

    mapping(address => uint256) private utilizationMultipliers; // asset => utilization multiplier , for maxOI control

    mapping(address => uint256) private lpSupply; // asset => lp supply
    mapping(address => uint256) private balances; // asset => balance
    mapping(address => mapping(address => uint256)) private userLpBalances; // asset => account => lp amount

    mapping(address => uint256) private bufferBalances; // asset => balance
    mapping(address => uint256) private lastPaid; // asset => timestamp
    mapping(address => uint256) public currentEpochRemainingBuffer; // asset => buffer amount

    mapping(address => bool) public whitelistedKeepers;
    mapping(address => bool) public whitelistedDepositer;
    mapping(address => bool) public whitelistedFundingAccount;
    mapping(address => int256) private globalUPLs; // asset => upl
    
    mapping(address => uint256) public feeReserves;  //treasury fees

    // Contracts
    AddressStorage public immutable addressStorage;
    PositionManager public positionManager;
    address public orderBookAddress;
    address public executorAddress;

        // Events
    event PoolDeposit(
        address indexed user,
        address indexed asset,
        uint256 amount,
        uint256 feeAmount,
        uint256 lpAmount,
        uint256 poolBalance
    );

    event DirectPoolDeposit(
        address indexed user,
        address indexed asset,
        uint256 amount,
        uint256 bufferToPoolAmount,
        uint256 poolBalance,
        uint256 bufferBalance
    );

    event PoolWithdrawal(
        address indexed user,
        address indexed asset,
        uint256 amount,
        uint256 feeAmount,
        uint256 lpAmount,
        uint256 poolBalance
    );

    event PoolPayIn(
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint256 amount,
        uint256 bufferToPoolAmount,
        uint256 poolBalance,
        uint256 bufferBalance
    );

    event PoolPayOut(
        address indexed user,
        address indexed asset,
        bytes10 market,
        uint256 amount,
        uint256 bufferToPoolAmount,
        uint256 poolBalance,
        uint256 bufferBalance
    );
    event FeeShareUpdated(uint256 feeShare);
    event BufferPayoutPeriodUpdated(uint256 period);
    event MaxLiquidityOrderTTLUpdated(uint256 maxLiquidityOrderTTL);    
    event UtilizationMultiplierUpdated(address indexed asset, uint256 utilizationMultiplier);
    event PublicDepositUpdated(bool isPublicDeposit);
    event WhitelistedKeeperUpdated(address indexed keeper, bool isActive);
    event WhitelistedFundingAccountUpdated(address indexed account, bool isActive);
    event WhitelistedDepositerUpdated(address indexed account, bool isActive);
    event Link(address orderBook, address positionManager, address executor);
    event WithdrawFees(address indexed asset, uint256 amount);
    event AssetSet(address indexed asset,Asset assetInfo);
    event MarketSet(bytes10 indexed market,Market marketInfo);
    event BalanceIncrement(address indexed asset, uint256 amount);
    event FeeAdded(address indexed asset, uint256 amount);
    event GlobalUPLSet(address indexed asset, int256 upl);
    event TransferIn(address indexed asset, address indexed from, uint256 amount);
    event TransferOut(address indexed asset, address indexed to, uint256 amount);
    event BufferToPool(address indexed asset,uint256 lastPaid,uint256 amountToSendPool);
    event AddOrder(uint32 indexed orderId, LiquidityType orderType);
    event RemoveOrder(uint32 indexed orderId, LiquidityType orderType);
    event OrderCancelled(uint32 indexed orderId, address indexed user, address executionFeeReceiver, string reason);
    event OrderCreated(
        uint32 indexed liquidityOrderId,
        address indexed user,
        address indexed asset,
        LiquidityType orderType,
        uint256 amount,
        uint256 minAmountMinusTax,
        uint256 executionFee,
        address fundingAccount
    );
    event OrderSkipped(uint32 indexed orderId, string reason);
    event OrderExecuted(uint32 indexed orderId, address indexed keeper, uint256 executionFee);
    event OrderExecutionFeeUpdated(uint64 orderExecutionFee);

    error Unauthorized(address account);

    

    /// @dev Only callable by PositionManager contract
    modifier onlyPositionManager() {
        if(msg.sender != address(positionManager))
            revert Unauthorized(msg.sender);
        _;
    }

    /// @dev Only callable by PositionManager or Executor contracts
    modifier onlyPositionManagerAndExecutor() {
        if(msg.sender != address(positionManager) && msg.sender != executorAddress)
            revert Unauthorized(msg.sender);
        _;
    }

    /// @dev Only callable by PositionManager or OrderBook contracts
    modifier onlyPositionManagerAndOrderBook() {
        if(msg.sender != address(positionManager) && msg.sender != orderBookAddress)
            revert Unauthorized(msg.sender);
        _;
    }

    /// @dev Initializes addressStorage address
    constructor(AddressStorage _addressStorage)  {
        addressStorage = _addressStorage;
    }

    /// @notice Initializes protocol contracts
    /// @dev Only callable by governance
    function link() external onlyGov {
        orderBookAddress = addressStorage.getAddress('OrderBook');
        positionManager = PositionManager(addressStorage.getAddress('PositionManager'));
        executorAddress = addressStorage.getAddress('Executor');
        emit Link(orderBookAddress, address(positionManager), executorAddress);
    }

    /// @notice withdraw treasury fees
    /// @dev Only callable by governance
    /// @param _asset  address of asset
    function withdrawFees(address _asset) external override onlyGov {
        uint256 amount = feeReserves[_asset];
        if (amount == 0) {
            return;
        }
        feeReserves[_asset] = 0;
        _transferOut(_asset, addressStorage.getAddress('treasury'), amount);
        emit WithdrawFees(_asset, amount);
    }

    /// @notice Set or update an asset
    /// @dev Only callable by governance
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _assetInfo Struct containing minSize and referencePriceFeed
    function setAsset(address _asset, Asset memory _assetInfo) external override onlyGov {
        assets[_asset] = _assetInfo;
        emit AssetSet(_asset, _assetInfo); 

        uint256 length = assetList.length;
        for (uint256 i; i < length; i++) {
            if (assetList[i] == _asset) return;
        }
        assetList.push(_asset);
        
    }

    /// @notice Set or update a market
    /// @dev Only callable by governance
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _marketInfo Market struct containing required market data
    function setMarket(bytes10 _market, Market memory _marketInfo) external override onlyGov {
        require(_marketInfo.fee <= MAX_FEE, '!max-fee');
        require(_marketInfo.maxLeverage >= 1, '!max-leverage');
        require(_marketInfo.maxDeviation <= MAX_DEVIATION, '!max-deviation');
        require(_marketInfo.liqThreshold <= MAX_LIQTHRESHOLD, '!max-liqthreshold');
        require(_marketInfo.minOrderAge <= MAX_MIN_ORDER_AGE, '!max-minorderage');
        require(_marketInfo.pythMaxAge >= MIN_PYTH_MAX_AGE, '!min-pythmaxage');

        markets[_market] = _marketInfo;
        emit MarketSet(_market, _marketInfo);

        uint256 length = marketList.length;
        for (uint256 i; i < length; i++) {
            // check if _market already exists, if yes return
            if (marketList[i] == _market) return;
        }
        marketList.push(_market);

        
    }

    /// @notice Set store fee
    /// @dev Only callable by governance
    /// @param _bps fee share in bps
    function setFeeShare(uint256 _bps) external override onlyGov {
        require(_bps < BPS_DIVIDER, '!bps');
        feeShare = _bps;
        emit FeeShareUpdated(_bps);
    }

    /// @notice Set buffer payout period
    /// @dev Only callable by governance
    /// @param _period Buffer payout period in seconds, default is 7 days (604800 seconds)
    function setBufferPayoutPeriod(uint256 _period) external override onlyGov {
        require(_period > 0, '!min-period');
        require(_period <= MAX_BUFFER_PAYOUT_PERIOD, '!max-period');
        bufferPayoutPeriod = _period;
        emit BufferPayoutPeriodUpdated(_period);
    }

    /// @notice Set utilization multiplier
    /// @dev Only callable by governance
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _utilizationMultiplier utilization multiplier in bps ,e.g. if it is 5000 , maxOI available = asset balance x %50, it can be greater than bps according to use
    function setUtilizationMultiplier(address _asset, uint256 _utilizationMultiplier) external override onlyGov {
        require(_utilizationMultiplier > 0, '!min-utilization-multiplier');
        utilizationMultipliers[_asset] = _utilizationMultiplier;
        emit UtilizationMultiplierUpdated(_asset,_utilizationMultiplier);
    }

    /// @notice Set buffer payout period
    /// @dev Only callable by governance
    /// @param _maxLiquidityOrderTTL Buffer payout period in seconds, default is 7 days (604800 seconds)
    function setMaxLiquidityOrderTTL(uint256 _maxLiquidityOrderTTL) external override onlyGov {
        require(_maxLiquidityOrderTTL > 0, '!min-order-ttl');
        require(_maxLiquidityOrderTTL <= 1 hours, '!max-order-ttl');
        maxLiquidityOrderTTL = _maxLiquidityOrderTTL;
        emit MaxLiquidityOrderTTLUpdated(_maxLiquidityOrderTTL);
    }

    /// @notice Set depositing public or private
    /// @dev Only callable by governance
    /// @param _isPublicDeposit whether depositin is public
    function setIsPublicDeposit(bool _isPublicDeposit) external override onlyGov {
        isPublicDeposit = _isPublicDeposit;
        emit PublicDepositUpdated(_isPublicDeposit);
    }

    /// @notice Whitelisted keeper that can set global upl
    /// @dev Only callable by governance
    /// @param _keeper Keeper address
    /// @param _isActive whether keeper is active
    function setWhitelistedKeeper(address _keeper, bool _isActive) external  onlyGov {
        whitelistedKeepers[_keeper] = _isActive;
        emit WhitelistedKeeperUpdated(_keeper,_isActive); 
    }

    /// @notice Set whitelisted Funding Account
    /// @dev Only callable by governance
    /// @param _account  accounts authorized to deposit on behalf of another user
    /// @param _isActive whether account is active
    function setWhitelistedFundingAccount(address _account, bool _isActive) external  onlyGov {
        whitelistedFundingAccount[_account] = _isActive;
        emit WhitelistedFundingAccountUpdated(_account,_isActive);
    }

    /// @notice Set whitelisted depositer
    /// @dev Only callable by governance
    /// @param _account  accounts authorized to deposit
    /// @param _isActive whether account is active
    function setWhitelistedDepositer(address _account, bool _isActive) external override onlyGov {
        whitelistedDepositer[_account] = _isActive;
        emit WhitelistedDepositerUpdated(_account,_isActive);
    }

    /// @notice Set keeper gas fee for order execution 
    /// @dev Only callable by governance
    /// @param _orderExecutionFee Fee with native token e.g. ETH
    function setOrderExecutionFee(uint64 _orderExecutionFee) external onlyGov {
        orderExecutionFee = _orderExecutionFee;
        emit OrderExecutionFeeUpdated(_orderExecutionFee);
    }


    /// @notice Increments pool balance
    /// @dev Only callable by PositionManager contract
    function incrementBalance(address _asset, uint256 _amount) external onlyPositionManager {
        balances[_asset] += _amount;
        emit BalanceIncrement(_asset, _amount);
    }

    /// @notice Increments treasury fees
    /// @dev Only callable by PositionManager contract
    function addFees(address _asset, uint256 _amount) external onlyPositionManager {
        feeReserves[_asset] += _amount;
        emit FeeAdded(_asset, _amount);
    }

    /// @notice Set global UPL, called by whitelisted keeper
    /// @param _assets Asset addresses
    /// @param _upls Corresponding total unrealized profit / loss
    function setGlobalUPLs(address[] calldata _assets, int256[] calldata _upls) external {
        if(!whitelistedKeepers[msg.sender])
            revert Unauthorized(msg.sender);
        _setGlobalUPLs(_assets, _upls);    
    }

    /// @notice Set global UPL, called by whitelisted keeper
    /// @param _assets Asset addresses
    /// @param _upls Corresponding total unrealized profit / loss
    function _setGlobalUPLs(address[] memory _assets, int256[] memory _upls) internal {
        for (uint256 i; i < _assets.length; i++) {
            globalUPLs[_assets[i]] = _upls[i];
            emit GlobalUPLSet(_assets[i], _upls[i]);
        }
    }


    /// @notice Credit trader loss to buffer and pay pool from buffer amount based on time and payout rate
    /// @dev Only callable by PositionManager and Executor contracts
    /// @param _user User which incurred trading loss
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _amount Amount of trader loss
    function creditTraderLoss(address _user, address _asset, bytes10 _market, uint256 _amount) external onlyPositionManagerAndExecutor {

        // pending buffer transfer to the pool
        uint256 amountToSendPool = _sendBufferToPool(_asset);

        // credit trader loss to buffer
        _incrementBufferBalance(_asset, _amount);

        // emit event
        emit PoolPayIn(
            _user,
            _asset,
            _market,
            _amount,
            amountToSendPool,
            balances[_asset],
            bufferBalances[_asset]
        );
    }

    /// @notice Pay out trader profit, from buffer first then pool if buffer is depleted
    /// @dev Only callable by PositionManager contract
    /// @param _user Address to send funds to
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _amount Amount of trader profit
    function debitTraderProfit(
        address _user,
        address _asset,
        bytes10 _market,
        uint256 _amount
    ) external onlyPositionManager {
        // return if profit = 0
        if (_amount == 0) return;

        uint256 bufferBalance = bufferBalances[_asset];

        // decrement buffer balance first
        _decrementBufferBalance(_asset, _amount);

        // decrement first next epoch buffer then current epoch remaining
        // if new buffer less than currentEpochRemaining, reduce currentEpochRemaining to buffer.
        if(bufferBalance  < currentEpochRemainingBuffer[_asset] + _amount) {
            currentEpochRemainingBuffer[_asset] = bufferBalances[_asset];
        }

        // if _amount is greater than available in the buffer, pay remaining from the pool
        if (_amount > bufferBalance) {
            uint256 diffToPayFromPool = _amount - bufferBalance;
            uint256 poolBalance = balances[_asset];
            require(diffToPayFromPool < poolBalance, '!pool-balance');
            _decrementBalance(_asset, diffToPayFromPool);
        }

        // pending buffer transfer to the pool
        uint256 amountToSendPool = _sendBufferToPool(_asset);

        // transfer profit out
        _transferOut(_asset, _user, _amount);

        // emit event
        emit PoolPayOut(_user, _asset, _market, _amount, amountToSendPool, balances[_asset], bufferBalances[_asset]);
    }

    /// @notice Transfers `_amount` of `_asset` in
    /// @dev Only callable by PositionManager or OrderBook contracts
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _from Address where asset is transferred from
    function transferIn(address _asset, address _from, uint256 _amount) external payable onlyPositionManagerAndOrderBook {
        _transferIn(_asset,_from,_amount);
    }

    /// @notice Transfers `_amount` of `_asset` out
    /// @dev Only callable by PositionManager or OrderBook contracts
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _to Address where asset is transferred to
    function transferOut(address _asset, address _to, uint256 _amount) external nonReentrant onlyPositionManagerAndOrderBook {
        _transferOut(_asset,_to,_amount);
    }

    /// @notice Direct Pool Deposit `_amount` of `_asset` into the pool via buffer
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _amount Amount to be deposited
    function directPoolDeposit(address _asset, uint256 _amount) external payable {
        require(_amount > 0, '!_amount');
        require(isSupported(_asset), '!_asset');

        // if _asset is ETH (address(0)), set _amount to msg.value
        if (_asset == address(0)) {
            require(msg.value > 0, '!msg.value');
            _amount = msg.value;
        } else {
            _transferIn(_asset, msg.sender, _amount);
        }

        // first the pending buffer will be transferred to the pool
        uint256 amountToSendPool = _sendBufferToPool(_asset);

        // direct deposit to buffer
        _incrementBufferBalance(_asset, _amount);

        // emit event
        emit DirectPoolDeposit(
            msg.sender, 
            _asset, 
            _amount, 
            amountToSendPool,
            balances[_asset],
            bufferBalances[_asset]
        );
    }    

    /// @notice Cancels order
    /// @param _orderId Order to cancel
    function cancelOrder(uint32 _orderId) external  {
        LiquidityOrder memory order = liquidityOrders[_orderId];
        require(order.amount > 0, '!order');
        require(order.user == msg.sender, '!user');
        _cancelOrder(_orderId, 'by-user', msg.sender);
    }

    /// @notice Submits a new deposit order
    /// @param _params Liquidity order to submit
    function depositRequest(
        LiquidityOrder memory _params
    ) external payable {
        if(!isPublicDeposit){
            require(whitelistedDepositer[msg.sender], '!whitelisted');
        }      
        require(_params.amount > 0, '!_amount');
        require(_params.amount >= _params.minAmountMinusTax, '!min-amount');
        require(isSupported(_params.asset), '!_asset');

        _params.user = _orderUser(_params);
        _params.orderType = LiquidityType.DEPOSIT;
        _params.timestamp = block.timestamp.toUint32();
        _params.executionFee = orderExecutionFee;

        // if _asset is ETH (address(0)), msg.value must be equal to amount + execution fee
        if (_params.asset == address(0)) {
            require(msg.value == _params.amount + _params.executionFee, '!msg.value');
        } else {
            require(msg.value == _params.executionFee, '!msg.value');
            _transferIn(_params.asset, msg.sender, _params.amount);
        }

        // Add order to store and emit event
        _params.liquidityOrderId = _add(_params);

        emit OrderCreated(
            _params.liquidityOrderId,
            _params.user,
            _params.asset,
            _params.orderType,
            _params.amount,
            _params.minAmountMinusTax,
            _params.executionFee,
            msg.sender
        );
    }

    /// @notice Submits a new withdraw order
    /// @param _params Liquidity order to submit
    function withdrawRequest(
        LiquidityOrder memory _params
    ) external payable {
        require(_params.amount > 0, '!_amount');
        require(msg.value == orderExecutionFee, '!msg.value');
        require(isSupported(_params.asset), '!_asset');

        _params.user = msg.sender;
        _params.orderType = LiquidityType.WITHDRAW;
        _params.timestamp = block.timestamp.toUint32();
        _params.executionFee = orderExecutionFee;

        // check pool balance and lp supply
        uint256 balance = balances[_params.asset];
        uint256 lpAssetSupply = lpSupply[_params.asset];
        require(balance > 0 && lpAssetSupply > 0, '!empty');

        // check user balance
        uint256 userBalance = getUserBalance(_params.asset, _params.user);
        if (_params.amount > userBalance) _params.amount = userBalance.toUint96();

        require(_params.amount >= _params.minAmountMinusTax, '!min-amount');

        // check available liquidity for open interests
        // if utilizationMultiplier is defined less than BPS_DIVIDER, allow user to withdraw with 1:1 ratio
        uint256 utilizationMultiplier = utilizationMultipliers[_params.asset];
        if(utilizationMultiplier < BPS_DIVIDER) utilizationMultiplier = BPS_DIVIDER;  
        require(positionManager.getAssetOI(_params.asset) <= (getAvailable(_params.asset) - _params.amount) * utilizationMultiplier / BPS_DIVIDER,"!not-available-liquidity");        

        // Add order to store and emit event
        _params.liquidityOrderId = _add(_params);

        emit OrderCreated(
            _params.liquidityOrderId,
            _params.user,
            _params.asset,
            _params.orderType,
            _params.amount,
            _params.minAmountMinusTax,
            _params.executionFee,
            address(0)
        );
    }

     /// @notice Order execution by keeper with global upls
    /// @dev Only callable by whitelistedKeepers
    /// @param _orderIds order id's to execute
    /// @param _assets Array of Asset
    /// @param _upls Array of Asset upls
    function executeOrders(
        uint32[] calldata _orderIds,
        address[] calldata _assets, 
        int256[] calldata _upls
    ) external nonReentrant {
        if(!whitelistedKeepers[msg.sender])
            revert Unauthorized(msg.sender);

        _setGlobalUPLs(_assets,_upls);

        for (uint256 i; i < _assets.length; i++) {
            // pending buffer transfer to the pool
            _sendBufferToPool(_assets[i]);         
        }

        for (uint256 i; i < _orderIds.length; i++) {
            (bool status, string memory reason) = _executeOrder(_orderIds[i], msg.sender);
            if (!status) _cancelOrder(_orderIds[i], reason, msg.sender);            
        }

    }

    /// @notice Returns asset struct of `_asset`
    /// @param _asset Asset address, e.g. address(0) for ETH
    function getAsset(address _asset) external view returns (Asset memory) {
        return assets[_asset];
    }

    /// @notice Get a list of all supported assets
    function getAssetList() external view returns (address[] memory) {
        return assetList;
    }

    /// @notice Get number of supported assets
    function getAssetCount() external view returns (uint256) {
        return assetList.length;
    }

    /// @notice Returns asset address at `_index`
    /// @param _index index of asset
    function getAssetByIndex(uint256 _index) external view returns (address) {
        return assetList[_index];
    }

    /// @notice Returns market struct of `market`
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    function getMarket(bytes10 _market) external view override returns (Market memory) {
        return markets[_market];
    }

    /// @notice Returns market struct array of specified markets
    /// @param _markets Array of market bytes10, e.g. ["0x4554482D555344000000", "0x4254432D555344000000"] for ETH-USD and BTC-USD
    function getMarketMany(bytes10[] calldata _markets) external view returns (Market[] memory) {
        uint256 length = _markets.length;
        Market[] memory _marketInfos = new Market[](length);
        for (uint256 i; i < length; i++) {
            _marketInfos[i] = markets[_markets[i]];
        }
        return _marketInfos;
    }

    /// @notice Returns market identifier at `index`
    /// @param index index of marketList
    function getMarketByIndex(uint256 index) external view returns (bytes10) {
        return marketList[index];
    }

    /// @notice Get a list of all supported markets
    function getMarketList() external view returns (bytes10[] memory) {
        return marketList;
    }

    /// @notice Get number of supported markets
    function getMarketCount() external view returns (uint256) {
        return marketList.length;
    }

    /// @notice Returns the sum of buffer and pool balance of `_asset`
    /// @param _asset Asset address, e.g. address(0) for ETH
    function getAvailable(address _asset) public view returns (uint256) {
        return balances[_asset] + bufferBalances[_asset];
    }

    /// @notice Returns the sum of buffer and pool balance of `_asset` multiplied by utilization multiplier
    /// @dev For available OI control for new position
    /// @param _asset Asset address, e.g. address(0) for ETH
    function getAvailableForOI(address _asset) external view returns (uint256) {
        return ((balances[_asset] + bufferBalances[_asset]) * utilizationMultipliers[_asset]) / BPS_DIVIDER;
    }

    
    /// @notice Returns amount of `_asset` in pool
    /// @param _asset Asset address, e.g. address(0) for ETH
    function getBalance(address _asset) external view returns (uint256) {
        return balances[_asset];
    }

    /// @notice Returns pool balances of `_assets`
    function getBalances(address[] calldata _assets) external view returns (uint256[] memory) {
        uint256 length = _assets.length;
        uint256[] memory _balances = new uint256[](length);

        for (uint256 i; i < length; i++) {
            _balances[i] = balances[_assets[i]];
        }

        return _balances;
    }

    /// @notice Returns buffer balances of `_assets`
    function getBufferBalances(address[] calldata _assets) external view returns (uint256[] memory) {
        uint256 length = _assets.length;
        uint256[] memory _balances = new uint256[](length);

        for (uint256 i; i < length; i++) {
            _balances[i] = bufferBalances[_assets[i]];
        }

        return _balances;
    }

    /// @notice Returns `_assets` balance of `account`
    function getUserBalances(address[] calldata _assets, address account) external view returns (uint256[] memory) {
        uint256 length = _assets.length;
        uint256[] memory _balances = new uint256[](length);

        for (uint256 i; i < length; i++) {
            _balances[i] = getUserBalance(_assets[i], account);
        }

        return _balances;
    }

    /// @notice Returns last time pool was paid
    /// @param _asset Asset address, e.g. address(0) for ETH
    function getLastPaid(address _asset) external view returns (uint256) {
        return lastPaid[_asset];
    }

    /// @notice Returns total amount of LP for `_asset`
    function getLpSupply(address _asset) external view returns (uint256) {
        return lpSupply[_asset];
    }

    /// @notice Returns total unrealized p/l for `_asset`
    function getGlobalUPL(address _asset) external view returns (int256) {
        return globalUPLs[_asset];
    }

        /// @notice Returns liquidity orders
    /// @param _length Amount of liquidity orders to return
    function getLiquidityOrders(uint256 _length) external view returns (LiquidityOrder[] memory) {
        uint32 orderlength = liquidityOrderIds.length().toUint32();
        if (_length > orderlength) _length = orderlength;

        LiquidityOrder[] memory _orders = new LiquidityOrder[](_length);

        for (uint256 i; i < _length; i++) {
            _orders[i] = liquidityOrders[liquidityOrderIds.at(i).toUint32()];
        }

        return _orders;
    }

    /// @notice Returns orders of `user`    
    function getUserOrders(address _user) external view returns (LiquidityOrder[] memory) {
        uint32 length = userLiquidityOrderIds[_user].length().toUint32();
        LiquidityOrder[] memory _orders = new LiquidityOrder[](length);

        for (uint256 i; i < length; i++) {
            _orders[i] = liquidityOrders[userLiquidityOrderIds[_user].at(i).toUint32()];
        }

        return _orders;
    }

    /// @notice Returns amount of market orders
    function getLiquidityOrderCount() external view returns (uint256) {
        return liquidityOrderIds.length();
    }

    /// @notice Returns order amount of `user`
    function getUserOrderCount(address _user) external view returns (uint256) {
        return userLiquidityOrderIds[_user].length();
    }

    /// @notice Get order user 
    /// @dev if sender is whitelisted funding account and order is suitable, The user is being made incoming in the params 
    /// @param _params Order params
    /// @return user order user
    function _orderUser(LiquidityOrder memory _params) internal view returns (address) {
        if(whitelistedFundingAccount[msg.sender]){
            require(_params.user != address(0) ,"funding-account-order-fail");
            return _params.user;        
        }
        return msg.sender;
    }

    /// @notice Returns pool deposit tax for `asset` and amount in bps
    function getDepositTaxBps(address _asset, uint256 _amount) public view returns (uint256) {
        uint256 taxBps;
        uint256 balance = balances[_asset];
        uint256 bufferBalance = bufferBalances[_asset];
        if (globalUPLs[_asset] - int256(bufferBalance) < 0) {
            taxBps = uint256(int256(BPS_DIVIDER) * (int256(bufferBalance) - globalUPLs[_asset]) / (int256(balance) + int256(_amount)));
        }
        return taxBps;
    }

    /// @notice Returns pool withdrawal tax for `asset` and amount in bps
    function getWithdrawalTaxBps(address _asset, uint256 _amount) public view returns (uint256) {
        uint256 taxBps;
        uint256 balance = balances[_asset];
        if (_amount > balance) return BPS_DIVIDER;
        uint256 bufferBalance = bufferBalances[_asset];
        if (globalUPLs[_asset] - int256(bufferBalance) > 0 ) {  
            if(_amount < balance)
                taxBps = uint256(int256(BPS_DIVIDER) * (globalUPLs[_asset] - int256(bufferBalance)) / (int256(balance) - int256(_amount)));
            else // _amount = balance => in fact if there is an open position, the last depositor cannot withdraw the entire balance, so this will not happen, but it may remain as a calculation 
                taxBps = uint256(int256(BPS_DIVIDER) * (globalUPLs[_asset] - int256(bufferBalance)) / int256(_amount));

        }
        return taxBps;
    }

    /// @notice Returns `_asset` balance of `_account`
    function getUserBalance(address _asset, address _account) public view returns (uint256) {
        if (lpSupply[_asset] == 0) return 0;
        return (userLpBalances[_asset][_account] * balances[_asset]) / lpSupply[_asset];
    }

    /// @notice Returns amount of LP of `_account` for `_asset`
    function getUserLpBalance(address _asset, address _account) external view returns (uint256) {
        return userLpBalances[_asset][_account];
    }

    /// @notice Returns true if `_asset` is supported
    /// @param _asset Asset address, e.g. address(0) for ETH
    function isSupported(address _asset) public view returns (bool) {
        return assets[_asset].minSize > 0;
    }

    /// @notice Returns amount of `_asset` in buffer
    function getBufferBalance(address _asset) external view returns (uint256) {
        return bufferBalances[_asset];
    }

    /// @notice Increments pool balance
    /// @dev Internal function
    function _incrementBalance(address _asset, uint256 _amount) internal {
        balances[_asset] += _amount;
    }

    /// @notice Decrements pool balance
    /// @dev Internal function
    function _decrementBalance(address _asset, uint256 _amount) internal {
        balances[_asset] = balances[_asset] <= _amount ? 0 : balances[_asset] - _amount;
    }

    /// @notice  Increments buffer balance
    /// @dev Internal function
    function _incrementBufferBalance(address _asset, uint256 _amount) internal {
        bufferBalances[_asset] += _amount;
    }

    /// @notice  Decrements buffer balance
    /// @dev Internal function
    function _decrementBufferBalance(address _asset, uint256 _amount) internal {
        bufferBalances[_asset] = bufferBalances[_asset] <= _amount ? 0 : bufferBalances[_asset] - _amount;
    }

    /// @notice  Updates `lastPaid`
    /// @dev Internal function
    function _setLastPaid(address _asset, uint256 _timestamp) internal {
        lastPaid[_asset] = _timestamp;
    }

    /// @notice Increments `lpSupply` and `userLpBalances`
    /// @dev Internal function
    function _incrementUserLpBalance(address _asset, address _user, uint256 _amount) internal {
        lpSupply[_asset] += _amount;

        unchecked {
            // Overflow not possible: balance + _amount is at most lpSupply + _amount, which is checked above.
            userLpBalances[_asset][_user] += _amount;
        }
    }

    /// @notice Decrements `lpSupply` and `userLpBalances`
    /// @dev Internal function
    function _decrementUserLpBalance(address asset, address _user, uint256 amount) internal {
        lpSupply[asset] = lpSupply[asset] <= amount ? 0 : lpSupply[asset] - amount;

        userLpBalances[asset][_user] = userLpBalances[asset][_user] <= amount
            ? 0
            : userLpBalances[asset][_user] - amount;
    }

    /// @notice Stream buffer balance progressively into the pool
    /// @dev Internal function
    function _sendBufferToPool(address _asset) internal returns (uint256) {
        // local variables
        uint256 bufferBalance = bufferBalances[_asset];
        uint256 lpAsset = lastPaid[_asset];
        uint256 currentTimestamp = block.timestamp;
        uint256 amountToSendPool;

        if(bufferBalance > 0 ){
            
            uint256 currentEpochStart = currentTimestamp / bufferPayoutPeriod * bufferPayoutPeriod;
            uint256 currentEpochRemaining = currentEpochRemainingBuffer[_asset];


            if(lpAsset < currentEpochStart - bufferPayoutPeriod){ // if the last transfer is before 2 periods, all buffer must be transferred
                amountToSendPool = bufferBalance;
                currentEpochRemaining = 0; 
            }else{
                if(lpAsset < currentEpochStart){   // previous epoch remaining         
                    amountToSendPool = currentEpochRemaining;
                    currentEpochRemaining = bufferBalance - amountToSendPool ;  //new epoch buffer amount
                    lpAsset = currentEpochStart;
                }

                if(currentEpochRemaining > 0 ){ 
                    uint256 transferAmount = currentEpochRemaining * (currentTimestamp - lpAsset) / (currentEpochStart + bufferPayoutPeriod - lpAsset);
                    if(transferAmount > currentEpochRemaining) transferAmount = currentEpochRemaining;
                    amountToSendPool += transferAmount;
                    currentEpochRemaining -= transferAmount;  
                }

                if (amountToSendPool >= bufferBalance) { //if true,  currentEpochRemainingBuffer must be empty
                    amountToSendPool = bufferBalance;
                    currentEpochRemaining = 0; 
                }    
            }

            currentEpochRemainingBuffer[_asset] = currentEpochRemaining;

            // update storage
            if(amountToSendPool > 0){
                _incrementBalance(_asset, amountToSendPool);
                _decrementBufferBalance(_asset, amountToSendPool);
            }        


        }

        _setLastPaid(_asset, currentTimestamp);

        emit BufferToPool(_asset, currentTimestamp, amountToSendPool);

        return amountToSendPool;
    }

    /// @notice Transfers `_amount` of `_asset` in
    /// @dev Internal function
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _from Address where asset is transferred from
    function _transferIn(address _asset, address _from, uint256 _amount) internal {
        if (_amount == 0 || _asset == address(0)) return;
        IERC20(_asset).safeTransferFrom(_from, address(this), _amount);
        emit TransferIn(_asset, _from, _amount);
    }

    /// @notice Transfers `_amount` of `_asset` out
    /// @dev Internal function
    /// @param _asset Asset address, e.g. address(0) for ETH
    /// @param _to Address where asset is transferred to
    function _transferOut(address _asset, address _to, uint256 _amount) internal{
        if (_amount == 0 || _to == address(0)) return;
        if (_asset == address(0)) {
            payable(_to).sendValue(_amount);
        } else {
            IERC20(_asset).safeTransfer(_to, _amount);
        }
        emit TransferOut(_asset, _to, _amount);
    }

        /// @dev Executes submitted order
    /// @param _orderId Order to execute
    /// @param _keeper keeper address
    /// @return status if true, order is not canceled.
    /// @return message if not blank, includes order revert message.
    function _executeOrder(
        uint32 _orderId,
        address _keeper
    ) internal returns (bool, string memory) { 
        LiquidityOrder memory order = liquidityOrders[_orderId];

        if (order.amount == 0) {
            return (false, '!order');
        }

        if (order.timestamp + maxLiquidityOrderTTL < block.timestamp ) {
            return (false, '!expired');
        } 
        
        if(order.orderType == LiquidityType.DEPOSIT ){  
            uint256 taxBps = getDepositTaxBps(order.asset, order.amount);
            if(taxBps >= BPS_DIVIDER){
                return (false, '!tax');
            }

            uint256 tax = (order.amount * taxBps) / BPS_DIVIDER;
            uint256 amountMinusTax = order.amount - tax;
            if(amountMinusTax < order.minAmountMinusTax){
                return(false, '!min-amount');
            }

            // pool share is equal to pool balance of _user divided by the total balance
            uint256 balance = balances[order.asset];
            uint256 lpAssetSupply = lpSupply[order.asset];
            uint256 lpAmount = balance == 0 || lpAssetSupply == 0 ? amountMinusTax : (amountMinusTax * lpAssetSupply) / balance;

            // increment balances
            _incrementUserLpBalance(order.asset, order.user, lpAmount);
            _incrementBalance(order.asset, order.amount);

            // emit event
            emit PoolDeposit(order.user, order.asset, order.amount, tax, lpAmount, balances[order.asset]);               

        } else if(order.orderType == LiquidityType.WITHDRAW ){
            // check available liquidity for open interests
            // if utilizationMultiplier is defined less than BPS_DIVIDER, allow user to withdraw with 1:1 ratio
            uint256 utilizationMultiplier = utilizationMultipliers[order.asset];
            if(utilizationMultiplier < BPS_DIVIDER) utilizationMultiplier = BPS_DIVIDER;  

            if((getAvailable(order.asset) - order.amount) * utilizationMultiplier / BPS_DIVIDER < positionManager.getAssetOI(order.asset)){
                return (false, '!not-available-liquidity');
            }

            // withdrawal tax
            uint256 taxBps = getWithdrawalTaxBps(order.asset, order.amount);
            if(taxBps >= BPS_DIVIDER){
                return (false, '!tax');
            }
            uint256 tax = (order.amount * taxBps) / BPS_DIVIDER;
            uint256 amountMinusTax = order.amount - tax;
            if(amountMinusTax < order.minAmountMinusTax){
                return(false, '!min-amount');
            }

            // LP amount
            uint256 balance = balances[order.asset];
            uint256 lpAssetSupply = lpSupply[order.asset];
            uint256 lpAmount = (order.amount * lpAssetSupply) / balance;

            // decrement balances
            _decrementUserLpBalance(order.asset, order.user, lpAmount);
            _decrementBalance(order.asset, amountMinusTax);

            // transfer funds out
            _transferOut(order.asset, order.user, amountMinusTax);

            // emit event
            emit PoolWithdrawal(order.user, order.asset, order.amount, tax, lpAmount, balances[order.asset]);             

        }
        _remove(_orderId);
        if(order.executionFee > 0){
            _transferOut(address(0), _keeper, order.executionFee);
        }
        emit OrderExecuted(_orderId, _keeper, order.executionFee);
        return (true, '');
    }    

    /// @notice  Adds order to storage
    /// @dev Internal function
    function _add(LiquidityOrder memory _order) internal returns (uint32) {
        uint32 nextOrderId = ++liquidityOid;
        _order.liquidityOrderId = nextOrderId;
        liquidityOrders[nextOrderId] = _order;
        userLiquidityOrderIds[_order.user].add(nextOrderId);
        liquidityOrderIds.add(nextOrderId);

        emit AddOrder(nextOrderId, _order.orderType);

        return nextOrderId;
    }

    /// @notice  Removes order from store
    /// @dev Internal function
    /// @param _orderId Order to remove
    function _remove(uint32 _orderId) internal {
        LiquidityOrder memory order = liquidityOrders[_orderId];
        if (order.amount == 0) return;
        userLiquidityOrderIds[order.user].remove(_orderId);
        liquidityOrderIds.remove(_orderId);
        emit RemoveOrder(_orderId, order.orderType);
        delete liquidityOrders[_orderId];        
    }



    /// @notice Cancels order
    /// @dev Internal function without access restriction
    /// @param _orderId Order to cancel
    /// @param _reason Cancellation reason
    /// @param _executionFeeReceiver Address of execution fee receiver
    function _cancelOrder(uint32 _orderId, string memory _reason, address _executionFeeReceiver) internal {
        LiquidityOrder memory order = liquidityOrders[_orderId];
        if (order.amount == 0) return;
        _remove(_orderId);

        bool isSentNative;

        if (order.orderType == LiquidityType.DEPOSIT) {
            isSentNative = order.asset == address(0) && order.user == _executionFeeReceiver;
            _transferOut(order.asset, order.user, order.amount + (isSentNative ? order.executionFee : 0));
        }

        if(order.executionFee > 0 && !isSentNative){
            _transferOut(address(0), _executionFeeReceiver, order.executionFee);
        }

        emit OrderCancelled(_orderId, order.user, _executionFeeReceiver, _reason);
    }

}

File 20 of 25 : AddressStorage.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

import '../utils/Governable.sol';
import './interfaces/IAddressStorage.sol';

/// @title AddressStorage
/// @notice General purpose address storage contract
/// @dev Access is restricted to governance
contract AddressStorage is Governable,IAddressStorage {
    mapping(bytes32 => address) public addressValues;

    event AddressSet(string indexed key, address value);
   
    /// @param key The key for the record
    /// @param value address to store
    /// @param overwrite Overwrites existing value if set to true   
    function setAddress(string calldata key, address value, bool overwrite) external override onlyGov returns (bool) {
        require(value != address(0), "!zero address");
        bytes32 hash = getHash(key);
        if (overwrite || addressValues[hash] == address(0)) {
            addressValues[hash] = value;
            emit AddressSet(key, value);
            return true;
        }
        return false;
    }

    
    /// @param key The key for the record
    function getAddress(string calldata key) external view returns (address) {
        return addressValues[getHash(key)];
    }

    /// @param key string to hash
    function getHash(string memory key) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(key));
    }
}

File 21 of 25 : Governable.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

/// @title Governable
/// @notice Basic access control mechanism, gov has access to certain functions
contract Governable {
    address public gov;

    event SetGov(address prevGov, address nextGov);

    error NotGovernance(address account);

    /// @dev Initializes the contract setting the deployer address as governance
    constructor() {
        _setGov(msg.sender);
    }

    /// @dev Reverts if called by any account other than gov
    modifier onlyGov() {
        if(msg.sender != gov) 
            revert NotGovernance(msg.sender);
        _;
    }

    /// @notice Sets a new governance address
    /// @dev Only callable by governance
    function setGov(address _gov) external onlyGov {
        require(_gov != address(0), "!zero address");
        _setGov(_gov);
    }

    /// @notice Sets a new governance address
    /// @dev Internal function without access restriction
    function _setGov(address _gov) internal {
        address prevGov = gov;
        gov = _gov;
        emit SetGov(prevGov, _gov);
    }
}

File 22 of 25 : IAddressStorage.sol
//SPDX-License-Identifier: BSD-3-Clause

pragma solidity 0.8.22;

interface IAddressStorage {
    function setAddress(string calldata key, address value, bool overwrite) external returns (bool) ;
}

File 23 of 25 : IReferencePriceFeed.sol
// SPDX-License-Identifier: BSD-3-Clause

pragma solidity 0.8.22;

interface IReferencePriceFeed {
    function getPrice(address _feed) external view returns (uint256);
}

File 24 of 25 : IStore.sol
//SPDX-License-Identifier: BSD-3-Clause

pragma solidity 0.8.22;


interface IStore {
    // Structs
    struct Asset {
        uint8 decimals;  
        uint88 minSize;  // minimum position size
        address referencePriceFeed;
    }

    struct Market {
        bytes32 name; // Market's full name, e.g. Bitcoin / U.S. Dollar
        bytes12 category; // crypto, fx, commodities, or indices
        address referencePriceFeed; // Reference Price feed contract address
        bytes32 pythFeed; // Pyth price feed id
        uint16 maxLeverage; // No decimals
        uint16 maxDeviation; // In bps, max price difference from oracle to referencePrice
        uint16 fee; // In bps. 10 = 0.1%
        uint16 liqThreshold; // In bps
        uint16 fundingFactor; // Yearly funding rate if OI is completely skewed to one side. In bps.
        uint8 minOrderAge; // Min order age before is can be executed. In seconds
        uint8 pythMaxAge; // Max Pyth submitted price age, in seconds
        bool isReduceOnly; // accepts only reduce only orders
        uint16 priceConfidenceThresholds;  // in bps. if threshold is higher than pyth price confidence, pyth price is used
        uint16 priceConfidenceMultipliers; // in bps. if threshold is lower than pyth price confidence, pyth price confidence multiplied by multiplier and add or remove pyth price according to min max price
        // e.g pyth price = 1000, pyth confidence = 2 (0.2%), threshold = 10 (0.1%), multiplier= 20000 (200%) => max price = 1000 + (2*2) = 1004, min price = 1000 - (2*2) = 996
    }


    function setAsset(address _asset, Asset memory _assetInfo) external;
    function setMarket(bytes10 _market, Market memory _marketInfo) external;
    function setFeeShare(uint256 _bps) external;
    function setBufferPayoutPeriod(uint256 _period) external;
    function setMaxLiquidityOrderTTL(uint256 _maxLiquidityOrderTTL) external;
    function setUtilizationMultiplier(address _asset, uint256 _utilizationMultiplier) external;
    function withdrawFees(address _asset) external;
    function setIsPublicDeposit(bool _isPublicDeposit) external;
    function setWhitelistedDepositer(address _account, bool _isActive) external;
    function getMarket(bytes10 _market) external view returns (Market memory);
}

File 25 of 25 : TradingValidator.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.22;

import '../utils/Governable.sol';
import './AddressStorage.sol';
import '../Store.sol';
import '../PositionManager.sol';


/// @title TradingValidator
/// @notice Implementation of risk mitigation measures such as maximum open interest and maximum pool drawdown
contract TradingValidator is Governable {
    // Constants
    uint256 public constant BPS_DIVIDER = 10000;

    mapping(bytes10 => mapping(address => uint256)) private maxOI; // market => asset => amount

    // Pool Risk Measures
    uint256 public poolHourlyDecay = 417; // bps  4.17% hourly, disappears after 24 hours
    mapping(address => int256) private poolProfitTracker; // asset => amount (amortized)
    mapping(address => uint256) private poolProfitLimit; // asset => bps
    mapping(address => uint256) private poolLastChecked; // asset => timestamp

    event Link(address store, address positionManager);
    event MaxOISet(address indexed asset, bytes10 indexed market, uint256 amount);
    event PoolHourlyDecaySet(uint256 poolHourlyDecay);
    event PoolProfitLimitSet(address indexed asset, uint256 poolProfitLimit);
 
    // Contracts
    AddressStorage public immutable addressStorage;
    Store public store;
    PositionManager public positionManager;

    /// @dev Only callable by PositionManager contract
    modifier onlyPositionManager() {
        require(msg.sender == address(positionManager), "!unauthorized");
        _;
    }

    /// @dev Initializes addressStorage address
    constructor(AddressStorage _addressStorage) {
        addressStorage = _addressStorage;
    }

    /// @notice Initializes protocol contracts
    /// @dev Only callable by governance
    function link() external onlyGov {
        store = Store(addressStorage.getAddress('Store'));
        positionManager = PositionManager(addressStorage.getAddress('PositionManager'));
        emit Link(address(store), address(positionManager));
    }

    /// @notice Set maximum open interest
    /// @notice Once current open interest exceeds this value, orders are no longer accepted
    /// @dev Only callable by governance
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _asset Address of base asset, e.g. address(0) for ETH
    /// @param _amount Max open interest to set
    function setMaxOI(bytes10 _market, address _asset, uint256 _amount) external onlyGov {
        require(_amount > 0, '!amount');
        maxOI[_market][_asset] = _amount;
        emit MaxOISet(_asset, _market, _amount);
    }

    /// @notice Set hourly pool decay
    /// @dev Only callable by governance
    /// @param _poolHourlyDecay Hourly pool decay in bps
    function setPoolHourlyDecay(uint256 _poolHourlyDecay) external onlyGov {
        require(_poolHourlyDecay < BPS_DIVIDER, '!poolHourlyDecay');
        poolHourlyDecay = _poolHourlyDecay;
        emit PoolHourlyDecaySet(_poolHourlyDecay);
    }

    /// @notice Set pool profit limit of `asset`
    /// @dev Only callable by governance
    /// @param _asset Address of asset, e.g. address(0) for ETH
    /// @param _poolProfitLimit Pool profit limit in bps
    function setPoolProfitLimit(address _asset, uint256 _poolProfitLimit) external onlyGov {
        require(_poolProfitLimit < BPS_DIVIDER, '!poolProfitLimit');
        poolProfitLimit[_asset] = _poolProfitLimit;
        emit PoolProfitLimitSet(_asset, _poolProfitLimit);
    }

    /// @notice Measures the net loss of a pool over time
    /// @notice Reverts if time-weighted drawdown is higher than the allowed profit limit
    /// @dev Only callable by other protocol contracts
    /// @dev Invoked by Positions.decreasePosition
    /// @param _asset Address of asset, e.g. address(0) for ETH
    /// @param _pnl Profit Loss amount
    function checkPoolDrawdown(address _asset, int256 _pnl) external onlyPositionManager {
        // Get available amount of `_asset` in the pool (pool balance + buffer balance)
        uint256 poolAvailable = store.getAvailable(_asset);

        // Get profit tracker, _pnl > 0 means trader win
        int256 profitTracker = getPoolProfitTracker(_asset) + _pnl;
        // get profit limit of pool
        uint256 profitLimit = poolProfitLimit[_asset];

        // update storage vars
        poolProfitTracker[_asset] = profitTracker;
        poolLastChecked[_asset] = block.timestamp;

        // return if profit limit or profit tracker is zero / less than zero
        if (profitLimit == 0 || profitTracker <= 0) return;

        // revert if profitTracker > profitLimit * available funds
        require(uint256(profitTracker) < (profitLimit * poolAvailable) / BPS_DIVIDER, '!pool-risk');
    }

    /// @notice Checks if maximum open interest is reached
    /// @param _market  Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _asset Address of base asset, e.g. address(0) for ETH
    function checkMaxOI(address _asset, bytes10 _market, uint256 _size) external view {
        uint256 openInterest = positionManager.getOI(_asset, _market);
        uint256 _maxOI = maxOI[_market][_asset];
        if (_maxOI > 0 && openInterest + _size > _maxOI) revert('!max-oi');

        uint256 openAssetInterest = positionManager.getAssetOI(_asset);
        uint256 assetMaxUtilization = store.getAvailableForOI(_asset);
        if (openAssetInterest + _size > assetMaxUtilization) revert('!_asset-max-oi');
    }

    /// @notice Get maximum open interest of `_market`
    /// @param _market Market, e.g. "0x4554482D555344000000" is bytes10 equivalent of "ETH-USD" 
    /// @param _asset Address of base asset, e.g. address(0) for ETH
    function getMaxOI(bytes10 _market, address _asset) external view returns (uint256) {
        return maxOI[_market][_asset];
    }

    /// @notice Returns pool profit tracker of `_asset`
    /// @dev Amortized every hour by 4.16% unless otherwise set
    function getPoolProfitTracker(address _asset) public view returns (int256) {
        int256 profitTracker = poolProfitTracker[_asset];
        uint256 lastCheckedHourId = poolLastChecked[_asset] / (1 hours);
        uint256 currentHourId = block.timestamp / (1 hours);

        if (currentHourId > lastCheckedHourId) {
            // hours passed since last check
            uint256 hoursPassed = currentHourId - lastCheckedHourId;
            if (hoursPassed >= (BPS_DIVIDER + poolHourlyDecay -1) / poolHourlyDecay) {
                profitTracker = 0;
            } else {
                profitTracker = (profitTracker * (int256(BPS_DIVIDER) - int256(poolHourlyDecay) * int256(hoursPassed))) / int256(BPS_DIVIDER);
            }
        }

        return profitTracker;
    }

    /// @notice Returns pool profit limit of `_asset`
    function getPoolProfitLimit(address _asset) external view returns (uint256) {
        return poolProfitLimit[_asset];
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract AddressStorage","name":"_addressStorage","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NotGovernance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"signed","type":"bool"},{"indexed":false,"internalType":"bool","name":"byGov","type":"bool"}],"name":"AccountApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"}],"name":"AddOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"ethSignedMessageHash","type":"bytes32"}],"name":"EthSignedMessageHashUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"store","type":"address"},{"indexed":false,"internalType":"address","name":"tradingValidator","type":"address"},{"indexed":false,"internalType":"address","name":"referralStorage","type":"address"},{"indexed":false,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"address","name":"positionManager","type":"address"}],"name":"Link","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxMarketOrderTTL","type":"uint256"}],"name":"MaxMarketOrderTTLUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxTriggerOrderTTL","type":"uint256"}],"name":"MaxTriggerOrderTTLUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"orderPaused","type":"bool"}],"name":"NewOrdersPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"executionFeeReceiver","type":"address"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bytes10","name":"market","type":"bytes10"},{"indexed":false,"internalType":"uint96","name":"margin","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"size","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"price","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isReduceOnly","type":"bool"},{"indexed":false,"internalType":"uint32","name":"expiry","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"indexed":false,"internalType":"address","name":"fundingAccount","type":"address"},{"indexed":false,"internalType":"uint64","name":"executionFee","type":"uint64"},{"indexed":false,"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"name":"OrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"orderExecutionFee","type":"uint64"}],"name":"OrderExecutionFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"processingPaused","type":"bool"}],"name":"ProcessingPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"orderId","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"orderType","type":"uint8"}],"name":"RemoveOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prevGov","type":"address"},{"indexed":false,"internalType":"address","name":"nextGov","type":"address"}],"name":"SetGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"WhitelistedFundingAccountUpdated","type":"event"},{"inputs":[],"name":"BPS_DIVIDER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TRAILING_STOP_PERCENTAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_order","type":"tuple"}],"name":"add","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressStorage","outputs":[{"internalType":"contract AddressStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedAccounts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areNewOrdersPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"},{"internalType":"string","name":"_reason","type":"string"},{"internalType":"address","name":"_executionFeeReceiver","type":"address"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_orderIds","type":"uint32[]"}],"name":"cancelOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"enableOrderByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ethSignedMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"get","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_orderIds","type":"uint32[]"}],"name":"getMany","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_length","type":"uint256"}],"name":"getMarketOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTriggerOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_length","type":"uint256"},{"internalType":"uint256","name":"_offset","type":"uint256"}],"name":"getTriggerOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserOrderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserOrders","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isProcessingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"link","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxMarketOrderTTL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTriggerOrderTTL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oid","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderExecutionFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"contract PositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralStorage","outputs":[{"internalType":"contract IReferralStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderId","type":"uint32"}],"name":"remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_areNewOrdersPaused","type":"bool"}],"name":"setAreNewOrdersPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"}],"name":"setEthSignedMessageHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isProcessingPaused","type":"bool"}],"name":"setIsProcessingPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxMarketOrderTTL","type":"uint256"}],"name":"setMaxMarketOrderTTL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTriggerOrderTTL","type":"uint256"}],"name":"setMaxTriggerOrderTTL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_orderExecutionFee","type":"uint64"}],"name":"setOrderExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setWhitelistedFundingAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"store","outputs":[{"internalType":"contract Store","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_params","type":"tuple"},{"internalType":"uint96","name":"_tpPrice","type":"uint96"},{"internalType":"uint96","name":"_slPrice","type":"uint96"},{"internalType":"uint16","name":"_trailingStopPercentage","type":"uint16"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"}],"name":"submitOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"bytes10","name":"market","type":"bytes10"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint96","name":"fee","type":"uint96"},{"internalType":"uint32","name":"timestamp","type":"uint32"},{"internalType":"uint32","name":"orderId","type":"uint32"},{"components":[{"internalType":"uint8","name":"orderType","type":"uint8"},{"internalType":"bool","name":"isReduceOnly","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"cancelOrderId","type":"uint32"},{"internalType":"uint64","name":"executionFee","type":"uint64"},{"internalType":"uint16","name":"trailingStopPercentage","type":"uint16"}],"internalType":"struct OrderBook.OrderDetail","name":"orderDetail","type":"tuple"}],"internalType":"struct OrderBook.Order","name":"_params","type":"tuple"},{"internalType":"uint96","name":"_tpPrice","type":"uint96"},{"internalType":"uint96","name":"_slPrice","type":"uint96"},{"internalType":"uint16","name":"_trailingStopPercentage","type":"uint16"},{"internalType":"bytes32","name":"_referralCode","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"submitOrderWithSignature","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"tradingValidator","outputs":[{"internalType":"contract TradingValidator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedFundingAccount","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60a034620000c757601f620046ed38819003918201601f19168301916001600160401b03831184841017620000cb57808492602094604052833981010312620000c757516001600160a01b038082168203620000c75760407f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f785915f54903360018060a01b03198316175f55825191168152336020820152a161012c60075562ed4e0060085560805260405161460d9081620000e082396080518181816113b601526116830152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe608060409080825260049081361015610016575f80fd5b60e05f35811c9182626cc35e14611d9457508162d84fd814611d5e5781630b87d88614611d055781630c5644af14611cc9578163101ce0e714611bff57816312d43a5114611bd857816315b0114114611bba578163180a98ed14611b925781631b92468d146119f65781631c4695f41461165a5781631d66bd881461163c57816320f510ca1461149d5781632769c28f146114765781632cc759671461143f578163346e89f114611421578163571fe991146113e5578163612e1488146113a257816363c69f081461120b57816368a3a1771461102157816369cb191414610e05578163791b98bc14610ddd5781637c4283bc14610dc157816380de66b714610da35781638ae7fea214610cdb5781638efd471614610b17578163975057e714610aef5781639a27994a14610a545781639b96eae514610a385781639d8e217714610a16578163a060a4dc146109be578163a8e9a53914610996578163bde4acda14610916578163c2582669146108f0578163cfad57a214610830578163d023fd5c146107a0578163d46a8d3d14610782578163d60c345c146106f5578163d8a26e3a146105c6578163e4223c5b1461052b578163e4fe5719146103c457508063e80225d01461039e578063e8ff863514610312578063eb4bb30d146102c9578063ed33a582146102a55763ef0bcc141461020f575f80fd5b346102a15760203660031901126102a157610228612010565b5f549091906001600160a01b0316330361028a577fbe784d108e8e78fa9f258361956e20859a59b9b3d092da0baa7fe208c181fbe860208460ff8560ff60401b600954911515841b169060ff60401b191617806009558251921c1615158152a1005b60249083519063988d1f0360e01b82523390820152fd5b5f80fd5b82346102a1575f3660031901126102a1576009548151911c60ff1615158152602090f35b82346102a1576102003660031901126102a15760209063ffffffff61030a6102f036612031565b61030560018060a01b03600e54163314612200565b6131fb565b915191168152f35b5090346102a15760203660031901126102a1578135916001600160401b0383168093036102a1575f546001600160a01b03163303610388577fe36ba27db2956162f9f3add30c33e17f0feb383d009583c4cd7867da7dde42ba60208484816001600160401b0319600954161760095551908152a1005b602491519063988d1f0360e01b82523390820152fd5b82346102a1575f3660031901126102a15760209060ff60095460481c1690519015158152f35b8390346102a1576103d436611de0565b916103de836123ab565b925f5b8181106103f9578551806103f58782611f1a565b0390f35b63ffffffff8061040a8385886122e2565b61041390612306565b165f52600190602090828252885f20918980519461043086611f63565b845490600160a01b6001900390818316885260a092831c858901528601549081168388015260109460606001600160501b0360b01b83881b16818a015260ff948c60f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528860c09e8f88828c1c16818701521c16818401521c90610100015283519b6104c08d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261051782876123fa565b5261052281866123fa565b506001016103e1565b5050346102a157816003193601126102a157610545611dca565b90602435918215158093036102a1575f546001600160a01b039290831633036105af5750916020917ff372b7fc2b1716fcc45bef3ebaad418b80fbda3771a0dbbef64cfa7050093e74931693845f52600b8352805f2060ff1981541660ff841617905551908152a2005b60249085519063988d1f0360e01b82523390820152fd5b8390346102a15760203660031901126102a1576106f3610200926105e8611db7565b926105f161232e565b5063ffffffff8094165f526001602052815f209082519461061186611f63565b60ff835460018060a01b0390818116895260a01c60208901526001850154908116868901526001600160501b0360b01b8160101b16606089015260f01c161515608087015260028301546001600160601b039283821660a0890152838260601c1660c0890152828260c01c16818901521c610100870152600384519361069685611f93565b01549160ff8316845260ff8360081c16151560208501528260101c1684840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152518092611e2c565bf35b82346102a15760603660031901126102a15761070f611db7565b602435906001600160401b03928383116102a157366023840112156102a1578201359283116102a15736602484840101116102a157604435916001600160a01b039081841684036102a1576107809461077061077a93601154163314612200565b60243692016121ca565b9061260e565b005b83346102a1575f3660031901126102a157602090600a549051908152f35b5050346102a15760203660031901126102a1576107bb611dca565b5f546001600160a01b039290831633036108195750165f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f8352600160208401523392a2005b60249084519063988d1f0360e01b82523390820152fd5b8284346102a15760203660031901126102a15761084b611dca565b5f546001600160a01b0380821692338490036108da57169081156108a7576001600160a01b03191681175f55825191825260208201527f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f7859250a1005b835162461bcd60e51b8152602081870152600d60248201526c217a65726f206164647265737360981b6044820152606490fd5b845163988d1f0360e01b81523381880152602490fd5b83346102a1575f3660031901126102a15760209063ffffffff5f5460a01c169051908152f35b5050346102a15760203660031901126102a157610931612010565b5f549091906001600160a01b0316330361028a577f6cc75cfd4b33bf26bdf4d64e2fe43562e8749917bf393cc51247676d98dd520360208460ff8560ff60481b60095491151560481b169060ff60481b1916179182600955519160481c1615158152a1005b83346102a1575f3660031901126102a15760115490516001600160a01b039091168152602090f35b8284346102a15760203660031901126102a1575f54823592906001600160a01b03163303610388577f31af123d7934bb99718a85cffb614a7bc155de49132ad4d79a2916ee85f240166020848481600a5551908152a1005b83346102a1575f3660031901126102a15760209051670de0b6b3a76400008152f35b83346102a1575f3660031901126102a157602090516107d08152f35b50506102803660031901126102a157610a6c36612031565b90610a75611fe2565b610a7d611ff9565b90610a8661201f565b92610a9860ff600954881c161561223c565b335f52600c60205260ff865f20541615610abc5750610780945061026435936129a6565b606490602087519162461bcd60e51b8352820152600d60248201526c085b9bdd08185c1c1c9bdd9959609a1b6044820152fd5b83346102a1575f3660031901126102a157600d5490516001600160a01b039091168152602090f35b905082346102a15760209160206003193601126102a157833590600380549463ffffffff9283610b4688612920565b16808611610cd3575b50610b59856123ab565b965f5b868110610b70578751806103f58b82611f1a565b81811015610cc057845f5285817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154610ba990612920565b165f526001808552885f2084868b5193610bc285611f63565b8c8b8b865492600160a01b60019003948585168a5260a094851c878b0152880154948516818a01526010926060926001600160501b0360b01b87861b16848c015260ff9485918c60f09960809889918c1c1615159101528c60028d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b610c578d611f93565b015498828a9384168d528360081c161515908c01521c16908801528d8560701c16908701528c8460901c16908601528260b01c6001600160401b0316908501521c90820152610120820152610cac828b6123fa565b52610cb7818a6123fa565b50600101610b5c565b60328a634e487b7160e01b5f525260245ffd5b945088610b4f565b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115610d7657600754821115610d43577ffc0ec2b149e86649398f2116f5e7196cfc456494a2ff5d37c7cedd9c46ca128c602083858160085551908152a1005b606490602084519162461bcd60e51b8352820152600d60248201526c0859dd17db585c9ad95d1d1d1b609a1b6044820152fd5b606490602084519162461bcd60e51b835282015260076024820152662167747a65726f60c81b6044820152fd5b83346102a1575f3660031901126102a1576020906005549051908152f35b83346102a1575f3660031901126102a157602090516127108152f35b83346102a1575f3660031901126102a157600e5490516001600160a01b039091168152602090f35b50506102a03660031901126102a157610e1d36612031565b90610e26611fe2565b610e2e611ff9565b90610e3761201f565b92610284356001600160401b0381116102a157366023820112156102a157610e6890369060248185013591016121ca565b90610e7a60ff600954891c161561223c565b335f52602091600c835260ff885f205416908115610f35575b5015610f06575061078095335f52600c8252805f209081549160ff831615610ec4575b5050505061026435936129a6565b60017fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47649360ff19161790555f815193600185528401523392a25f808080610eb6565b60649187519162461bcd60e51b8352820152600b60248201526a085b9bdd081cda59db995960aa1b6044820152fd5b9050600a54610f4482826134a2565b600581949294101561100e57908a91159384610ffb575b508315610f6c575b5050505f610e93565b5f92935090610fa2610fb0849383519283918a830195630b135d3f60e11b998a88526024850152604484015260648301906125d0565b03601f198101835282611fc1565b5190335afa90610fbe6134d7565b82610fee575b82610fd4575b50505f8881610f63565b90915083818051810103126102a157830151145f80610fca565b9150838251101591610fc4565b6001600160a01b0316331493505f610f5b565b602185634e487b7160e01b5f525260245ffd5b8284346102a157806003193601126102a157813592602493602435938195600590600554926110598861105386612920565b9661240e565b63ffffffff8096168091116111f7575b50611073896123ab565b975f5b8a811061108a578851806103f58c82611f1a565b611094828261240e565b868110156111e5578790865f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001546110cd90612920565b165f5260016020818152888b5f20918b8d8051956110ea87611f63565b855491600160a01b6001900391828416895260a093841c868a0152870154918216818901526010956060916001600160501b0360b01b84891b16838b015260ff9560f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528760c09e8f88828d1c16818701521c16818401521c90610100015283519b61117a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c16908901528560701c16908701528d8460901c16908601528260b01c6001600160401b0316908501521c908201526101208201526111d1828c6123fa565b526111dc818b6123fa565b50600101611076565b84603285634e487b7160e01b5f52525ffd5b611204919950889061241b565b9789611069565b83346102a157602090816003193601126102a1576001600160a01b0380611230611dca565b1692835f526002906002815263ffffffff908161124f865f2054612920565b169361125a856123ab565b965f5b868110611271578751806103f58b82611f1a565b815f528584528481895f20906112869161348d565b919054600392831b1c61129890612920565b165f5260018086528b86898c5f20938d8051966112b488611f63565b8654928b8416895260a093841c868a0152870154918b8316818a01528d6010926060936001600160501b0360b01b86821b16858d015260ff918c60f099846080998c1c161515898301528d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b6113398d611f93565b015498828a9384168d528360081c161515908c01521c16908801528c8560701c16908701528b8460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261138e828b6123fa565b52611399818a6123fa565b5060010161125d565b83346102a1575f3660031901126102a157517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b83346102a15760203660031901126102a1576020906001600160a01b0361140a611dca565b165f52600c825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a1576020906008549051908152f35b83346102a15760203660031901126102a1576020906001600160a01b03611464611dca565b165f5260028252805f20549051908152f35b83346102a1575f3660031901126102a1576020906001600160401b03600954169051908152f35b8390346102a1576114ad36611de0565b60ff92916114c260ff600954871c161561223c565b5f5b63ffffffff908181168481101561078057826114e18287876122e2565b6114ea90612306565b165f526001888760208381528a835f20918451809961150882611f63565b845490600160a01b6001900398898316845260a092831c8585015286015490898216898501526010986060906001600160501b0360b01b848c1b168287015260f09588608095881c1615158582015260028a0154986001600160601b039c828b8f948a019e8f86831690528860c09e8f88828c1c16818701521c16818401521c90610100015283519b61159a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c908201526101208701525116151590611603948261162e575b5050611608575b506122b9565b6114c4565b61161e6116196116289287876122e2565b612306565b339061077a612272565b876115fd565b9091505116331489806115f6565b83346102a1575f3660031901126102a1576020906003549051908152f35b8284346102a1575f3660031901126102a1575f546001600160a01b0392908316330361038857827f00000000000000000000000000000000000000000000000000000000000000001682519163bf40fac160e01b938484526020938483820152600560248201526453746f726560d81b60448201528481606481875afa9081156119ec579087915f916119b4575b5016946001600160601b0360a01b948686600d541617600d5582518281528185820152601060248201526f2a3930b234b733ab30b634b230ba37b960811b60448201528181606481895afa9081156118e4579089915f9161197c575b5016918287600f541617600f5583518181528286820152600f60248201526e526566657272616c53746f7261676560881b604482015282816064818a5afa90811561197257908a915f9161193a575b5016948588601054161760105584518281528382820152600860248201526722bc32b1baba37b960c11b604482015283816064818b5afa908115611930578492918c915f916118ee575b5093606491849516998a8c6011541617601155885195869485938452830152600f60248301526e2837b9b4ba34b7b726b0b730b3b2b960891b60448301525afa9081156118e4575f9161187b575b50907ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f9860a098979695949392168096600e541617600e55825196875286015284015260608301526080820152a1005b9796959493929180915088813d83116118dd575b6118998183611fc1565b810103126102a1577ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f986118ce60a0996122a5565b9192939495969798509861182b565b503d61188f565b84513d5f823e3d90fd5b94938092508591503d8311611929575b6119088183611fc1565b810103126102a15760648493928c61192086956122a5565b919550916117dd565b503d6118fe565b86513d5f823e3d90fd5b809250848092503d831161196b575b6119538183611fc1565b810103126102a1576119658a916122a5565b8b611793565b503d611949565b85513d5f823e3d90fd5b809250838092503d83116119ad575b6119958183611fc1565b810103126102a1576119a789916122a5565b8a611744565b503d61198b565b809250868092503d83116119e5575b6119cd8183611fc1565b810103126102a1576119df87916122a5565b886116e8565b503d6119c3565b82513d5f823e3d90fd5b8284346102a157602090816003193601126102a157611a13611db7565b93611a2560ff600954841c161561223c565b63ffffffff908186165f5260018452825f2091835191611a4483611f63565b83549360018060a01b0394858116855260a01c8785015260ff6001820154868116888701526001600160501b0360b01b8160101b16606087015260f01c16151560808501526002810154926001600160601b039360a08601938582168552858260601c1660c0880152828260c01c16818801521c6101008601526003875192611acc84611f93565b01549060ff8216835260ff8260081c16151589840152848260101c1688840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152511615611b665751163303611b3d57610780338561077a612272565b5162461bcd60e51b815291820152600560248201526410bab9b2b960d91b604482015260649150fd5b50505162461bcd60e51b815291820152600660248201526510b7b93232b960d11b604482015260649150fd5b83346102a1575f3660031901126102a157600f5490516001600160a01b039091168152602090f35b83346102a1575f3660031901126102a1576020906007549051908152f35b83346102a1575f3660031901126102a1575f5490516001600160a01b039091168152602090f35b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115611c9b57600854821015611c67577fb389411734ec38434cf45d5ddb15e04046d44ade0e97b4dd832a6e781f81015a602083858160075551908152a1005b606490602084519162461bcd60e51b8352820152600e60248201526d085b1d17dd1c9a59d9d95c9d1d1b60921b6044820152fd5b606490602084519162461bcd60e51b835282015260086024820152672167745f7a65726f60c01b6044820152fd5b83346102a15760203660031901126102a1576020906001600160a01b03611cee611dca565b165f52600b825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a157335f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f83525f60208401523392a2005b346102a15760203660031901126102a157610780611d7a611db7565b611d8f60018060a01b03600e54163314612200565b612428565b346102a1575f3660031901126102a1576010546001600160a01b03168152602090f35b6004359063ffffffff821682036102a157565b600435906001600160a01b03821682036102a157565b9060206003198301126102a1576004356001600160401b03928382116102a157806023830112156102a15781600401359384116102a15760248460051b830101116102a1576024019190565b61ffff60c06101e09260018060a01b038082511686526020820151906001600160601b03809216602088015260408301511660408701526001600160501b0360b01b60608301511660608701526080820151151560808701528060a08301511660a08701528083830151168387015260e08201519063ffffffff80921660e088015261010082818501511690880152610120809301519260ff8451169088015260208301511515610140880152604083015116610160870152806060830151166101808701526080820151166101a08601526001600160401b0360a0820151166101c0860152015116910152565b60209060206040818301928281528551809452019301915f5b828110611f41575050505090565b909192938261020082611f576001948951611e2c565b01950193929101611f33565b61014081019081106001600160401b03821117611f7f57604052565b634e487b7160e01b5f52604160045260245ffd5b60e081019081106001600160401b03821117611f7f57604052565b6001600160401b038111611f7f57604052565b90601f801991011681019081106001600160401b03821117611f7f57604052565b61020435906001600160601b03821682036102a157565b61022435906001600160601b03821682036102a157565b6004359081151582036102a157565b610244359061ffff821682036102a157565b906102006003198301126102a1576040519161204c83611f63565b6001600160a01b0390839060043583811681036102a15782526001600160601b039260243584811681036102a157602084015260443590811681036102a15760408301526064356001600160b01b0319811681036102a157606083015260843580151581036102a157608083015260a43583811681036102a15760a083015260c43583811681036102a15760c083015263ffffffff9060e43582811681036102a15760e0840152610104359082821682036102a15760e0916101008501526101231901126102a1576040519261212184611f93565b6101243560ff811681036102a15784526101443580151581036102a15760208501526101643590811681036102a15760408401526101843581811681036102a15760608401526101a43590811681036102a15760808301526101c4356001600160401b03811681036102a15760a08301526101e4359061ffff821682036102a1576101209160c08401520152565b6001600160401b038111611f7f57601f01601f191660200190565b9291926121d6826121af565b916121e46040519384611fc1565b8294818452818301116102a1578281602093845f960137010152565b1561220757565b60405162461bcd60e51b815260206004820152600d60248201526c085d5b985d5d1a1bdc9a5e9959609a1b6044820152606490fd5b1561224357565b60405162461bcd60e51b8152602060048201526007602482015266085c185d5cd95960ca1b6044820152606490fd5b60405190604082018281106001600160401b03821117611f7f576040526007825266313c96bab9b2b960c91b6020830152565b51906001600160a01b03821682036102a157565b63ffffffff8091169081146122ce5760010190565b634e487b7160e01b5f52601160045260245ffd5b91908110156122f25760051b0190565b634e487b7160e01b5f52603260045260245ffd5b3563ffffffff811681036102a15790565b6001600160401b038111611f7f5760051b60200190565b6040519061233b82611f63565b815f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526101206040519161238083611f93565b5f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401520152565b906123b582612317565b6123c26040519182611fc1565b82815280926123d3601f1991612317565b01905f5b8281106123e357505050565b6020906123ee61232e565b828285010152016123d7565b80518210156122f25760209160051b010190565b919082018092116122ce57565b919082039182116122ce57565b63ffffffff80911690815f5260209060018252604090815f209282519461244e86611f63565b84549260018060a01b0393848116885260a01c8388015260ff6001870154858116878a01526001600160501b0360b01b8160101b1660608a015260f01c161515608088015260028601546001600160601b039160a08901918381168352838160601c1660c08b0152818160c01c1660e08b015260e01c6101008a015260038751986124d88a611f93565b01549060ff8216895260ff8260081c161515868a0152838260101c16888a0152808260701c1660608a01528160901c1660808901526001600160401b038160b01c1660a089015260f01c60c088015261012088019687525116156125ad57807fa79f5bd225998a95d4de82c67e16e86bc61191a4faa2dbcb0e1a89886af5a0058360ff6003986001975f9b51168b5260028352612577858a8d206143bd565b5061258185614202565b5061258b856142fe565b505151168751908152a285525282208281558260018201558260028201550155565b505050505050565b9190916001600160601b03808094169116019182116122ce57565b91908251928382525f5b8481106125fa575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016125da565b905f9263ffffffff9182841693845f526001602052604095865f20918780519361263785611f63565b80549760018060a01b03988981168752602087019060a01c815260018301549460ff858901968c811688526001600160501b0360b01b8160101b1660608b015260f01c16151560808901526002840154906001600160601b03809460a08b0190828516825260c08c0194838160601c1686528c60e0838360c01c1691015260e01c6101008d015260038951986126cc8a611f93565b01549860ff8a16895260ff8a60081c16151560208a0152838a60101c1690890152808960701c1660608901528860901c1660808801526001600160401b0397888160b01c1660a089015260f01c60c08801526101208b01968752511615612910578b936127398a92612428565b5f9760208751015115612834575b50505050505060a0905101511690811515908161282b575b506127ae575b50508392917f7ffce3199c8273ece91032d8f943315778e942eff22f8f7d8a7da972073a58b1946127a99251169680519485941684528060208501528301906125d0565b0390a3565b85600d541690813b1561282757885163078d3b7960e01b81525f60048201526001600160a01b03871660248201526001600160401b039190911660448201529082908290606490829084905af1801561281d5715612765576128108291611fae565b61281a5780612765565b80fd5b88513d84823e3d90fd5b8280fd5b9050155f61275f565b8598509461287e9293949551169384159081612901575b828293612867939b80600d5416975116985116915116906125b5565b90156128fa57868060a088510151165b16906125b5565b90803b156102a1578d5163078d3b7960e01b81526001600160a01b0393841660048201529390921660248401526001600160601b031660448301525f908290606490829084905af180156128f0576128dc575b808087818c94612747565b6128e7919450611fae565b5f9260a06128d1565b8b513d5f823e3d90fd5b865f612877565b84518a168e8b1614915061284b565b5050505050505050505050505050565b63ffffffff90818111612931571690565b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608490fd5b519081151582036102a157565b51906001600160601b03821682036102a157565b9291905f946001600160601b0390818316159283801580916131f0575b80156131e3575b6131d3575b8861012097888a0198895160208101511590818015906131c5575b612f7f575b506001600160a01b039a91508a9050612a078c613506565b16808c523303612f73575b6001600160401b0391826009541660a0835101528260a08351015116988c612a3981613603565b99809150151580612f66575b612ef0575b50508590612ee5575b8015612ed8575b612baf575b505050505050612a7e935084604087015116155f14612ba7571661240e565b803410612b7557612a8f903461241b565b9182612a9c575b50505050565b511690804710612b30578280929181925af1612ab66134d7565b5015612ac5575f808080612a96565b60405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269216d73672d76616c756560b01b6044820152606490fd5b50508461240e565b87604083949596979a93510151168681612db0575b8d915080612da5575b612d1f575b88612a7e9b8760808194018051159052606087510163ffffffff815116612d17575b505016918215612cc557505090612c2691604084510152600283515260016020845101528360a084510151169061240e565b96612c308b613603565b50945b612c81575b50505063ffffffff8116151580612c72575b612c58575b88818080612a5f565b81612c66612c6b93836141c9565b6141c9565b5f80612c4f565b5063ffffffff82161515612c4a565b612cb294979350908660a09216604082510152600181515260016020825101528b60c082510152510151169061240e565b92612cbc87613603565b505f8080612c38565b61ffff919792509992991680612cdc575b50612c33565b612d0592999196506003845152600160208551015260c0845101528360a084510151169061240e565b96612d0f8b613603565b50945f612cd6565b528f5f612bf4565b6080919a955001511580159081612d97575b8115612d7c575b5015612d47578b93988b612bd2565b60405162461bcd60e51b815260206004820152600d60248201526c085d1c1cdb0b5a5b9d985b1a59609a1b6044820152606490fd5b905080612d8a575b5f612d38565b5086841687891610612d84565b8589168a8a16119150612d31565b50888b161515612bcd565b612e53575b50878a1680612dc5575b86612bc4565b90945060808c01511580159182612e42575b8215612e20575b505015612ded578b935f612dbf565b60405162461bcd60e51b815260206004820152600b60248201526a085cdb0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612e31575b505f80612dde565b90508760408451015116105f612e29565b8451604001518a1681109250612dd7565b90945060808c01511580159182612ecb575b8215612eae575b505015612e7b578b935f612db5565b60405162461bcd60e51b815260206004820152600b60248201526a085d1c0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612ebf575b505f80612e6c565b9050878916105f612eb7565b809250898b161191612e65565b5061ffff81161515612a5a565b50878a161515612a53565b909192939495508c806010541692511690823b156102a15760445f928360405195869485936356b4b2ad60e01b8552600485015260248401525af18015612f5b57612f42575b908c8e95949392612a4a565b612f50919493929d50611fae565b5f9b9091925f612f36565b6040513d5f823e3d90fd5b508d601054161515612a45565b5f608082510152612a12565b51929350909160ff1660031461316f575b50600e546040808c015160608d0151825163052ec72760e51b81523360048201526001600160a01b0392831660248201526001600160b01b0319918216604482015292949093909291839183916064918391165afa9283156118e4575f93613093575b505050866080820151161561306057810151151560808b015115151461301d579081808c936129ef565b5162461bcd60e51b815260206004820152601760248201527f217265647563652d77726f6e672d646972656374696f6e0000000000000000006044820152606490fd5b815162461bcd60e51b815260206004820152600c60248201526b10b73796b837b9b4ba34b7b760a11b6044820152606490fd5b909180935082813d8311613168575b6130ac8183611fc1565b810103126102a15783519283018381106001600160401b03821117611f7f5784526130d6826122a5565b8352602082015190811681036102a15760208301526130f6838201612985565b83830152613106606082016122a5565b606083015261311760808201612992565b608083015260a081015163ffffffff811681036102a15760a083015261313f60c08201612992565b60c083015261315060e08201612992565b60e083015261010080910151908201525f8080612ff3565b503d6130a2565b613180575f60408a5101525f612f90565b60405162461bcd60e51b815260206004820152601a60248201527f21747261696c696e672d73746f702d6d7573742d7265647563650000000000006044820152606490fd5b50600360ff825116146129ea565b5f60206101208a015101526129cf565b5061ffff871615156129ca565b5083861615156129c3565b5f547f6802d0b735cb1927bc8d580cfb1f7e0307b427272c81609ccce7a80ab5afb070602060ff63ffffffff613235818660a01c166122b9565b958187169563ffffffff60a01b8860a01b169063ffffffff60a01b1916175f55610100810190868252865f5260018552610120600360405f2060018060a01b039485855116956001600160601b0360a01b8a87015160a01b1687178355600183019060408701511681546060880151918b60f01b60808a0151151560f01b16926001600160501b0360a01b9060101c16918c60f81b1617171790556001600160601b0360a086015116906001600160601b0360601b60c087015160601b169163ffffffff60c01b60e088015160c01b169163ffffffff60e01b905160e01b16921717176002820155019101926133a98451918683511684549061ff008a860151151560081b166dffffffffffffffffffffffff0000604087015160101b169163ffffffff60701b606088015160701b169371ffffffffffffffffffffffffffffffffffff1916171717178455608083015116839081549063ffffffff60901b9060901b169063ffffffff60901b1916179055565b60a08101519082549060c061ffff60f01b91015160f01b16916001600160401b0360b01b9060b01b169060018060b01b0316171790555f52600283526133f28560405f2061458a565b508051518216613414576134058561453c565b505b515116604051908152a290565b61341d856144e9565b50613407565b6003548110156122f25760035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01905f90565b6005548110156122f25760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b80548210156122f2575f5260205f2001905f90565b9060418151145f146134ce576134ca91602082015190606060408401519301515f1a9061446e565b9091565b50505f90600290565b3d15613501573d906134e8826121af565b916134f66040519384611fc1565b82523d5f602084013e565b606090565b335f52600b60205260ff60405f20541661351f57503390565b80516001600160a01b039190821615158061359b575b8061358a575b1561354557511690565b60405162461bcd60e51b815260206004820152601a60248201527f66756e64696e672d6163636f756e742d6f726465722d6661696c0000000000006044820152606490fd5b5060ff61012082015151161561353b565b506020610120820151015115613535565b519060ff821682036102a157565b519061ffff821682036102a157565b156135d057565b60405162461bcd60e51b815260206004820152600b60248201526a216d61782d65787069727960a81b6044820152606490fd5b905f91600460ff610120830151511610156141965761012081015160ff81511660018114801561418c575b156141155750506001600160601b036040610120830151015116156140e7575b600d54604082810151905163185c596360e11b81526001600160a01b03918216600482015291169390606081602481885afa8015612f5b575f9061405f575b60200180516affffffffffffffffffffff925082161561402a5760206101208501510151159182159261400d575b505015613fdc576060820151604051637061696b60e01b81526001600160b01b031990911660048201526101c081602481885afa908115612f5b575f91613e9f575b5061ffff60808201511615613e695763ffffffff606061012085015101511680613de0575b5063ffffffff6080610120850151015116613d75575b63ffffffff61374642612920565b1660e08401526001600160601b0360a08401511661ffff60c083015116026001600160601b0381169081036122ce576001600160601b03612710820411613d215761271090046001600160601b031660c0840152610120830151602001515f9586929091156139a55750508160208401525b6001600160401b0360a061012085015101511690811515908161399c575b5061392a575b50506137e7816131fb565b63ffffffff8116610100830152600160a01b60019003825116600160a01b60019003604084015116916001600160501b0360b01b6060850151169060208501516001600160601b031660a08601516001600160601b03166101208701519060408201516001600160601b031660c08901516001600160601b031660808a01511515845160ff16906020860151151592606087015163ffffffff1694608088015163ffffffff169660a08901516001600160401b03169860c0015161ffff16996040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e08701526101008601526101208501523361014085015261016084015261018083015263ffffffff16906101a07feb2d670c93b7063878c5ca9fe0ec6eb06b845e9e4250f6cf7e0087680422efb291a4610100015163ffffffff169190565b600d546001600160a01b031690813b156128275760405163e4652f4960e01b81525f60048201523360248201526001600160401b0382166044820152918391839160649183915af1801561399157156137dc576139878291611fae565b61281a57806137dc565b6040513d84823e3d90fd5b9050155f6137d6565b909550610160810151613ce6576001600160601b0360208501511615613cb7576001600160601b0360a08501511690670de0b6b3a76400009180830290838204036122ce576001600160601b03602087015116908115613ca3570490828210613c6e576080015161ffff168281029281159184041417156122ce5711613c3957600f546040840151606085015160a08601516001600160601b0316926001600160a01b03928316926001600160b01b03199092169116803b156102a1575f9260649160405195869485936318512afb60e21b85526004850152602484015260448301525afa8015612f5b57613c26575b50613abb6001600160601b036020850151166001600160601b0360c086015116906125b5565b60408401519095906001600160a01b031680613ba25750505060018060a01b03600d54166001600160401b03906001600160601b03613b058360a0610120880151015116886125b5565b1691613b2960018060a01b036040870151169160a0610120880151015116886125b5565b92823b15613b9e5760405163e4652f4960e01b81526001600160a01b039290921660048301523360248301526001600160601b0393909316604482015291839183916064918391905af1801561399157908291613b8a575b505060016137b8565b613b9390611fae565b61281a57805f613b81565b8480fd5b813b15613c225760405163e4652f4960e01b81526001600160a01b039190911660048201523360248201526001600160601b03871660448201529083908290606490829084905af18015613c1757908391613bff575b50506137b8565b613c0890611fae565b613c1357815f613bf8565b5080fd5b6040513d85823e3d90fd5b8380fd5b613c31919250611fae565b5f905f613a95565b60405162461bcd60e51b815260206004820152600d60248201526c216d61782d6c6576657261676560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600d60248201526c216d696e2d6c6576657261676560981b6044820152606490fd5b634e487b7160e01b5f52601260045260245ffd5b60405162461bcd60e51b815260206004820152600760248201526610b6b0b933b4b760c91b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272216d61726b65742d7265647563652d6f6e6c7960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608490fd5b82516001600160a01b03165f9081526002602090815260408083206101208701516080015163ffffffff1684526001019091529020546137385760405162461bcd60e51b815260206004820152600960248201526821757365722d6f636f60b81b6044820152606490fd5b4211613e3457613dff4263ffffffff606061012087015101511661241b565b6101208401515160ff16613e2157613e1b9060075410156135c9565b5f613722565b613e2f9060085410156135c9565b613e1b565b60405162461bcd60e51b815260206004820152600d60248201526c216578706972792d76616c756560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d216d61726b65742d65786973747360901b6044820152606490fd5b90506101c0813d6101c011613fd4575b81613ebd6101c09383611fc1565b810103126102a157604051906101c082018281106001600160401b03821117611f7f576040528051825260208101516001600160a01b0319811681036102a1576020830152613f0e604082016122a5565b604083015260608101516060830152613f29608082016135ba565b6080830152613f3a60a082016135ba565b60a0830152613f4b60c082016135ba565b60c0830152613f5c60e082016135ba565b60e0830152610100613f6f8183016135ba565b90830152613f8061012082016135ac565b610120830152610140613f948183016135ac565b90830152610160613fa6818301612985565b90830152610180613fb88183016135ba565b90830152613fca6101a08092016135ba565b908201525f6136fd565b3d9150613eaf565b60405162461bcd60e51b8152602060048201526009602482015268216d696e2d73697a6560b81b6044820152606490fd5b9091506001600160601b0360a08501511691511611155f806136bb565b60405162461bcd60e51b815260206004820152600d60248201526c2161737365742d65786973747360981b6044820152606490fd5b506060813d6060116140df575b8161407960609383611fc1565b810103126102a157604051606081018181106001600160401b03821117611f7f576040526140a6826135ac565b81526020820151916affffffffffffffffffffff831683036102a15760406140d59160209485850152016122a5565b604082015261368d565b3d915061406c565b60405162461bcd60e51b815260206004820152600660248201526521707269636560d01b6044820152606490fd5b600314614123575b5061364e565b60c0015161ffff16801515908161417f575b5015614141575f61411d565b60405162461bcd60e51b8152602060048201526016602482015275085d1c985a5b1a5b99cb5cdd1bdc0b5a5b9d985b1a5960521b6044820152606490fd5b6107d0915011155f614135565b506002811461362e565b60405162461bcd60e51b815260206004820152600b60248201526a216f726465722d7479706560a81b6044820152606490fd5b63ffffffff165f908152600160205260409020600301805463ffffffff60901b191660909290921b63ffffffff60901b16919091179055565b805f526004908160205260405f20548015155f146142f7575f19908082018181116142e457600354908382019182116142d157808203614288575b50505060035480156142755781019061425582613423565b909182549160031b1b191690556003555f526020525f6040812055600190565b603184634e487b7160e01b5f525260245ffd5b6142bc6142976142a693613423565b90549060031b1c928392613423565b819391549060031b91821b915f19901b19161790565b90555f528360205260405f20555f808061423d565b601186634e487b7160e01b5f525260245ffd5b601185634e487b7160e01b5f525260245ffd5b5050505f90565b5f8181526006602052604090205480156143b7575f19908082018181116122ce57600554908382019182116122ce57808203614383575b505050600554801561436f5781019061434d82613458565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6143a16143926142a693613458565b90549060031b1c928392613458565b90555f52600660205260405f20555f8080614335565b50505f90565b906001820191815f528260205260405f2054908115155f14614466575f19918083018181116122ce578254908482019182116122ce57808203614431575b5050508054801561436f57820191614413838361348d565b909182549160031b1b19169055555f526020525f6040812055600190565b6144516144416142a6938661348d565b90549060031b1c9283928661348d565b90555f528460205260405f20555f80806143fb565b505050505f90565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116144de576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15612f5b575f516001600160a01b038116156144d657905f90565b505f90600190565b505050505f90600390565b805f52600660205260405f2054155f1461453757600554600160401b811015611f7f576145206142a6826001859401600555613458565b9055600554905f52600660205260405f2055600190565b505f90565b805f52600460205260405f2054155f1461453757600354600160401b811015611f7f576145736142a6826001859401600355613423565b9055600354905f52600460205260405f2055600190565b5f8281526001820160205260409020546143b757805490600160401b821015611f7f57826145c26142a684600180960185558461348d565b90558054925f520160205260405f205560019056fea264697066735822122073544964512f140197b410bd1dbd91dfadf04ff2d797ff6ecaaec45306031c5164736f6c63430008160033000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839

Deployed Bytecode

0x608060409080825260049081361015610016575f80fd5b60e05f35811c9182626cc35e14611d9457508162d84fd814611d5e5781630b87d88614611d055781630c5644af14611cc9578163101ce0e714611bff57816312d43a5114611bd857816315b0114114611bba578163180a98ed14611b925781631b92468d146119f65781631c4695f41461165a5781631d66bd881461163c57816320f510ca1461149d5781632769c28f146114765781632cc759671461143f578163346e89f114611421578163571fe991146113e5578163612e1488146113a257816363c69f081461120b57816368a3a1771461102157816369cb191414610e05578163791b98bc14610ddd5781637c4283bc14610dc157816380de66b714610da35781638ae7fea214610cdb5781638efd471614610b17578163975057e714610aef5781639a27994a14610a545781639b96eae514610a385781639d8e217714610a16578163a060a4dc146109be578163a8e9a53914610996578163bde4acda14610916578163c2582669146108f0578163cfad57a214610830578163d023fd5c146107a0578163d46a8d3d14610782578163d60c345c146106f5578163d8a26e3a146105c6578163e4223c5b1461052b578163e4fe5719146103c457508063e80225d01461039e578063e8ff863514610312578063eb4bb30d146102c9578063ed33a582146102a55763ef0bcc141461020f575f80fd5b346102a15760203660031901126102a157610228612010565b5f549091906001600160a01b0316330361028a577fbe784d108e8e78fa9f258361956e20859a59b9b3d092da0baa7fe208c181fbe860208460ff8560ff60401b600954911515841b169060ff60401b191617806009558251921c1615158152a1005b60249083519063988d1f0360e01b82523390820152fd5b5f80fd5b82346102a1575f3660031901126102a1576009548151911c60ff1615158152602090f35b82346102a1576102003660031901126102a15760209063ffffffff61030a6102f036612031565b61030560018060a01b03600e54163314612200565b6131fb565b915191168152f35b5090346102a15760203660031901126102a1578135916001600160401b0383168093036102a1575f546001600160a01b03163303610388577fe36ba27db2956162f9f3add30c33e17f0feb383d009583c4cd7867da7dde42ba60208484816001600160401b0319600954161760095551908152a1005b602491519063988d1f0360e01b82523390820152fd5b82346102a1575f3660031901126102a15760209060ff60095460481c1690519015158152f35b8390346102a1576103d436611de0565b916103de836123ab565b925f5b8181106103f9578551806103f58782611f1a565b0390f35b63ffffffff8061040a8385886122e2565b61041390612306565b165f52600190602090828252885f20918980519461043086611f63565b845490600160a01b6001900390818316885260a092831c858901528601549081168388015260109460606001600160501b0360b01b83881b16818a015260ff948c60f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528860c09e8f88828c1c16818701521c16818401521c90610100015283519b6104c08d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261051782876123fa565b5261052281866123fa565b506001016103e1565b5050346102a157816003193601126102a157610545611dca565b90602435918215158093036102a1575f546001600160a01b039290831633036105af5750916020917ff372b7fc2b1716fcc45bef3ebaad418b80fbda3771a0dbbef64cfa7050093e74931693845f52600b8352805f2060ff1981541660ff841617905551908152a2005b60249085519063988d1f0360e01b82523390820152fd5b8390346102a15760203660031901126102a1576106f3610200926105e8611db7565b926105f161232e565b5063ffffffff8094165f526001602052815f209082519461061186611f63565b60ff835460018060a01b0390818116895260a01c60208901526001850154908116868901526001600160501b0360b01b8160101b16606089015260f01c161515608087015260028301546001600160601b039283821660a0890152838260601c1660c0890152828260c01c16818901521c610100870152600384519361069685611f93565b01549160ff8316845260ff8360081c16151560208501528260101c1684840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152518092611e2c565bf35b82346102a15760603660031901126102a15761070f611db7565b602435906001600160401b03928383116102a157366023840112156102a1578201359283116102a15736602484840101116102a157604435916001600160a01b039081841684036102a1576107809461077061077a93601154163314612200565b60243692016121ca565b9061260e565b005b83346102a1575f3660031901126102a157602090600a549051908152f35b5050346102a15760203660031901126102a1576107bb611dca565b5f546001600160a01b039290831633036108195750165f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f8352600160208401523392a2005b60249084519063988d1f0360e01b82523390820152fd5b8284346102a15760203660031901126102a15761084b611dca565b5f546001600160a01b0380821692338490036108da57169081156108a7576001600160a01b03191681175f55825191825260208201527f53351836099c03ffc3b1727d8abd4b0222afa87d4ed76ae3102d51369ef7f7859250a1005b835162461bcd60e51b8152602081870152600d60248201526c217a65726f206164647265737360981b6044820152606490fd5b845163988d1f0360e01b81523381880152602490fd5b83346102a1575f3660031901126102a15760209063ffffffff5f5460a01c169051908152f35b5050346102a15760203660031901126102a157610931612010565b5f549091906001600160a01b0316330361028a577f6cc75cfd4b33bf26bdf4d64e2fe43562e8749917bf393cc51247676d98dd520360208460ff8560ff60481b60095491151560481b169060ff60481b1916179182600955519160481c1615158152a1005b83346102a1575f3660031901126102a15760115490516001600160a01b039091168152602090f35b8284346102a15760203660031901126102a1575f54823592906001600160a01b03163303610388577f31af123d7934bb99718a85cffb614a7bc155de49132ad4d79a2916ee85f240166020848481600a5551908152a1005b83346102a1575f3660031901126102a15760209051670de0b6b3a76400008152f35b83346102a1575f3660031901126102a157602090516107d08152f35b50506102803660031901126102a157610a6c36612031565b90610a75611fe2565b610a7d611ff9565b90610a8661201f565b92610a9860ff600954881c161561223c565b335f52600c60205260ff865f20541615610abc5750610780945061026435936129a6565b606490602087519162461bcd60e51b8352820152600d60248201526c085b9bdd08185c1c1c9bdd9959609a1b6044820152fd5b83346102a1575f3660031901126102a157600d5490516001600160a01b039091168152602090f35b905082346102a15760209160206003193601126102a157833590600380549463ffffffff9283610b4688612920565b16808611610cd3575b50610b59856123ab565b965f5b868110610b70578751806103f58b82611f1a565b81811015610cc057845f5285817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154610ba990612920565b165f526001808552885f2084868b5193610bc285611f63565b8c8b8b865492600160a01b60019003948585168a5260a094851c878b0152880154948516818a01526010926060926001600160501b0360b01b87861b16848c015260ff9485918c60f09960809889918c1c1615159101528c60028d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b610c578d611f93565b015498828a9384168d528360081c161515908c01521c16908801528d8560701c16908701528c8460901c16908601528260b01c6001600160401b0316908501521c90820152610120820152610cac828b6123fa565b52610cb7818a6123fa565b50600101610b5c565b60328a634e487b7160e01b5f525260245ffd5b945088610b4f565b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115610d7657600754821115610d43577ffc0ec2b149e86649398f2116f5e7196cfc456494a2ff5d37c7cedd9c46ca128c602083858160085551908152a1005b606490602084519162461bcd60e51b8352820152600d60248201526c0859dd17db585c9ad95d1d1d1b609a1b6044820152fd5b606490602084519162461bcd60e51b835282015260076024820152662167747a65726f60c81b6044820152fd5b83346102a1575f3660031901126102a1576020906005549051908152f35b83346102a1575f3660031901126102a157602090516127108152f35b83346102a1575f3660031901126102a157600e5490516001600160a01b039091168152602090f35b50506102a03660031901126102a157610e1d36612031565b90610e26611fe2565b610e2e611ff9565b90610e3761201f565b92610284356001600160401b0381116102a157366023820112156102a157610e6890369060248185013591016121ca565b90610e7a60ff600954891c161561223c565b335f52602091600c835260ff885f205416908115610f35575b5015610f06575061078095335f52600c8252805f209081549160ff831615610ec4575b5050505061026435936129a6565b60017fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47649360ff19161790555f815193600185528401523392a25f808080610eb6565b60649187519162461bcd60e51b8352820152600b60248201526a085b9bdd081cda59db995960aa1b6044820152fd5b9050600a54610f4482826134a2565b600581949294101561100e57908a91159384610ffb575b508315610f6c575b5050505f610e93565b5f92935090610fa2610fb0849383519283918a830195630b135d3f60e11b998a88526024850152604484015260648301906125d0565b03601f198101835282611fc1565b5190335afa90610fbe6134d7565b82610fee575b82610fd4575b50505f8881610f63565b90915083818051810103126102a157830151145f80610fca565b9150838251101591610fc4565b6001600160a01b0316331493505f610f5b565b602185634e487b7160e01b5f525260245ffd5b8284346102a157806003193601126102a157813592602493602435938195600590600554926110598861105386612920565b9661240e565b63ffffffff8096168091116111f7575b50611073896123ab565b975f5b8a811061108a578851806103f58c82611f1a565b611094828261240e565b868110156111e5578790865f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001546110cd90612920565b165f5260016020818152888b5f20918b8d8051956110ea87611f63565b855491600160a01b6001900391828416895260a093841c868a0152870154918216818901526010956060916001600160501b0360b01b84891b16838b015260ff9560f09680608096891c161515868d015260028b0154998c8b6001600160601b03948582168b8401528760c09e8f88828d1c16818701521c16818401521c90610100015283519b61117a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c16908901528560701c16908701528d8460901c16908601528260b01c6001600160401b0316908501521c908201526101208201526111d1828c6123fa565b526111dc818b6123fa565b50600101611076565b84603285634e487b7160e01b5f52525ffd5b611204919950889061241b565b9789611069565b83346102a157602090816003193601126102a1576001600160a01b0380611230611dca565b1692835f526002906002815263ffffffff908161124f865f2054612920565b169361125a856123ab565b965f5b868110611271578751806103f58b82611f1a565b815f528584528481895f20906112869161348d565b919054600392831b1c61129890612920565b165f5260018086528b86898c5f20938d8051966112b488611f63565b8654928b8416895260a093841c868a0152870154918b8316818a01528d6010926060936001600160501b0360b01b86821b16858d015260ff918c60f099846080998c1c161515898301528d0154809c6001600160601b03968783168c85015260c09e8f89828d1c16818701521c16818401521c90610100015284519b6113398d611f93565b015498828a9384168d528360081c161515908c01521c16908801528c8560701c16908701528b8460901c16908601528260b01c6001600160401b0316908501521c9082015261012082015261138e828b6123fa565b52611399818a6123fa565b5060010161125d565b83346102a1575f3660031901126102a157517f000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c8396001600160a01b03168152602090f35b83346102a15760203660031901126102a1576020906001600160a01b0361140a611dca565b165f52600c825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a1576020906008549051908152f35b83346102a15760203660031901126102a1576020906001600160a01b03611464611dca565b165f5260028252805f20549051908152f35b83346102a1575f3660031901126102a1576020906001600160401b03600954169051908152f35b8390346102a1576114ad36611de0565b60ff92916114c260ff600954871c161561223c565b5f5b63ffffffff908181168481101561078057826114e18287876122e2565b6114ea90612306565b165f526001888760208381528a835f20918451809961150882611f63565b845490600160a01b6001900398898316845260a092831c8585015286015490898216898501526010986060906001600160501b0360b01b848c1b168287015260f09588608095881c1615158582015260028a0154986001600160601b039c828b8f948a019e8f86831690528860c09e8f88828c1c16818701521c16818401521c90610100015283519b61159a8d611f93565b6003015499828d8c94851690528360081c161515908d01521c1690890152818660701c16908801528460901c16908601528260b01c6001600160401b0316908501521c908201526101208701525116151590611603948261162e575b5050611608575b506122b9565b6114c4565b61161e6116196116289287876122e2565b612306565b339061077a612272565b876115fd565b9091505116331489806115f6565b83346102a1575f3660031901126102a1576020906003549051908152f35b8284346102a1575f3660031901126102a1575f546001600160a01b0392908316330361038857827f000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c8391682519163bf40fac160e01b938484526020938483820152600560248201526453746f726560d81b60448201528481606481875afa9081156119ec579087915f916119b4575b5016946001600160601b0360a01b948686600d541617600d5582518281528185820152601060248201526f2a3930b234b733ab30b634b230ba37b960811b60448201528181606481895afa9081156118e4579089915f9161197c575b5016918287600f541617600f5583518181528286820152600f60248201526e526566657272616c53746f7261676560881b604482015282816064818a5afa90811561197257908a915f9161193a575b5016948588601054161760105584518281528382820152600860248201526722bc32b1baba37b960c11b604482015283816064818b5afa908115611930578492918c915f916118ee575b5093606491849516998a8c6011541617601155885195869485938452830152600f60248301526e2837b9b4ba34b7b726b0b730b3b2b960891b60448301525afa9081156118e4575f9161187b575b50907ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f9860a098979695949392168096600e541617600e55825196875286015284015260608301526080820152a1005b9796959493929180915088813d83116118dd575b6118998183611fc1565b810103126102a1577ff4aa8c967b4ad917e7d98f8e11fe6ab3e28b2eec7f6423ba8229c8f21375116f986118ce60a0996122a5565b9192939495969798509861182b565b503d61188f565b84513d5f823e3d90fd5b94938092508591503d8311611929575b6119088183611fc1565b810103126102a15760648493928c61192086956122a5565b919550916117dd565b503d6118fe565b86513d5f823e3d90fd5b809250848092503d831161196b575b6119538183611fc1565b810103126102a1576119658a916122a5565b8b611793565b503d611949565b85513d5f823e3d90fd5b809250838092503d83116119ad575b6119958183611fc1565b810103126102a1576119a789916122a5565b8a611744565b503d61198b565b809250868092503d83116119e5575b6119cd8183611fc1565b810103126102a1576119df87916122a5565b886116e8565b503d6119c3565b82513d5f823e3d90fd5b8284346102a157602090816003193601126102a157611a13611db7565b93611a2560ff600954841c161561223c565b63ffffffff908186165f5260018452825f2091835191611a4483611f63565b83549360018060a01b0394858116855260a01c8785015260ff6001820154868116888701526001600160501b0360b01b8160101b16606087015260f01c16151560808501526002810154926001600160601b039360a08601938582168552858260601c1660c0880152828260c01c16818801521c6101008601526003875192611acc84611f93565b01549060ff8216835260ff8260081c16151589840152848260101c1688840152808260701c1660608401528160901c1660808301526001600160401b038160b01c1660a083015260f01c60c0820152610120840152511615611b665751163303611b3d57610780338561077a612272565b5162461bcd60e51b815291820152600560248201526410bab9b2b960d91b604482015260649150fd5b50505162461bcd60e51b815291820152600660248201526510b7b93232b960d11b604482015260649150fd5b83346102a1575f3660031901126102a157600f5490516001600160a01b039091168152602090f35b83346102a1575f3660031901126102a1576020906007549051908152f35b83346102a1575f3660031901126102a1575f5490516001600160a01b039091168152602090f35b5050346102a15760203660031901126102a1575f54813591906001600160a01b0316330361028a578115611c9b57600854821015611c67577fb389411734ec38434cf45d5ddb15e04046d44ade0e97b4dd832a6e781f81015a602083858160075551908152a1005b606490602084519162461bcd60e51b8352820152600e60248201526d085b1d17dd1c9a59d9d95c9d1d1b60921b6044820152fd5b606490602084519162461bcd60e51b835282015260086024820152672167745f7a65726f60c01b6044820152fd5b83346102a15760203660031901126102a1576020906001600160a01b03611cee611dca565b165f52600b825260ff815f20541690519015158152f35b83346102a1575f3660031901126102a157335f52600c602052805f20600160ff198254161790557fd468a14a59cdb33f2dc218d1bc99264ac3af91e78bb8a8fbfb9f341d56bf47648151915f83525f60208401523392a2005b346102a15760203660031901126102a157610780611d7a611db7565b611d8f60018060a01b03600e54163314612200565b612428565b346102a1575f3660031901126102a1576010546001600160a01b03168152602090f35b6004359063ffffffff821682036102a157565b600435906001600160a01b03821682036102a157565b9060206003198301126102a1576004356001600160401b03928382116102a157806023830112156102a15781600401359384116102a15760248460051b830101116102a1576024019190565b61ffff60c06101e09260018060a01b038082511686526020820151906001600160601b03809216602088015260408301511660408701526001600160501b0360b01b60608301511660608701526080820151151560808701528060a08301511660a08701528083830151168387015260e08201519063ffffffff80921660e088015261010082818501511690880152610120809301519260ff8451169088015260208301511515610140880152604083015116610160870152806060830151166101808701526080820151166101a08601526001600160401b0360a0820151166101c0860152015116910152565b60209060206040818301928281528551809452019301915f5b828110611f41575050505090565b909192938261020082611f576001948951611e2c565b01950193929101611f33565b61014081019081106001600160401b03821117611f7f57604052565b634e487b7160e01b5f52604160045260245ffd5b60e081019081106001600160401b03821117611f7f57604052565b6001600160401b038111611f7f57604052565b90601f801991011681019081106001600160401b03821117611f7f57604052565b61020435906001600160601b03821682036102a157565b61022435906001600160601b03821682036102a157565b6004359081151582036102a157565b610244359061ffff821682036102a157565b906102006003198301126102a1576040519161204c83611f63565b6001600160a01b0390839060043583811681036102a15782526001600160601b039260243584811681036102a157602084015260443590811681036102a15760408301526064356001600160b01b0319811681036102a157606083015260843580151581036102a157608083015260a43583811681036102a15760a083015260c43583811681036102a15760c083015263ffffffff9060e43582811681036102a15760e0840152610104359082821682036102a15760e0916101008501526101231901126102a1576040519261212184611f93565b6101243560ff811681036102a15784526101443580151581036102a15760208501526101643590811681036102a15760408401526101843581811681036102a15760608401526101a43590811681036102a15760808301526101c4356001600160401b03811681036102a15760a08301526101e4359061ffff821682036102a1576101209160c08401520152565b6001600160401b038111611f7f57601f01601f191660200190565b9291926121d6826121af565b916121e46040519384611fc1565b8294818452818301116102a1578281602093845f960137010152565b1561220757565b60405162461bcd60e51b815260206004820152600d60248201526c085d5b985d5d1a1bdc9a5e9959609a1b6044820152606490fd5b1561224357565b60405162461bcd60e51b8152602060048201526007602482015266085c185d5cd95960ca1b6044820152606490fd5b60405190604082018281106001600160401b03821117611f7f576040526007825266313c96bab9b2b960c91b6020830152565b51906001600160a01b03821682036102a157565b63ffffffff8091169081146122ce5760010190565b634e487b7160e01b5f52601160045260245ffd5b91908110156122f25760051b0190565b634e487b7160e01b5f52603260045260245ffd5b3563ffffffff811681036102a15790565b6001600160401b038111611f7f5760051b60200190565b6040519061233b82611f63565b815f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f60c08201525f60e08201525f6101008201526101206040519161238083611f93565b5f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401520152565b906123b582612317565b6123c26040519182611fc1565b82815280926123d3601f1991612317565b01905f5b8281106123e357505050565b6020906123ee61232e565b828285010152016123d7565b80518210156122f25760209160051b010190565b919082018092116122ce57565b919082039182116122ce57565b63ffffffff80911690815f5260209060018252604090815f209282519461244e86611f63565b84549260018060a01b0393848116885260a01c8388015260ff6001870154858116878a01526001600160501b0360b01b8160101b1660608a015260f01c161515608088015260028601546001600160601b039160a08901918381168352838160601c1660c08b0152818160c01c1660e08b015260e01c6101008a015260038751986124d88a611f93565b01549060ff8216895260ff8260081c161515868a0152838260101c16888a0152808260701c1660608a01528160901c1660808901526001600160401b038160b01c1660a089015260f01c60c088015261012088019687525116156125ad57807fa79f5bd225998a95d4de82c67e16e86bc61191a4faa2dbcb0e1a89886af5a0058360ff6003986001975f9b51168b5260028352612577858a8d206143bd565b5061258185614202565b5061258b856142fe565b505151168751908152a285525282208281558260018201558260028201550155565b505050505050565b9190916001600160601b03808094169116019182116122ce57565b91908251928382525f5b8481106125fa575050825f602080949584010152601f8019910116010190565b6020818301810151848301820152016125da565b905f9263ffffffff9182841693845f526001602052604095865f20918780519361263785611f63565b80549760018060a01b03988981168752602087019060a01c815260018301549460ff858901968c811688526001600160501b0360b01b8160101b1660608b015260f01c16151560808901526002840154906001600160601b03809460a08b0190828516825260c08c0194838160601c1686528c60e0838360c01c1691015260e01c6101008d015260038951986126cc8a611f93565b01549860ff8a16895260ff8a60081c16151560208a0152838a60101c1690890152808960701c1660608901528860901c1660808801526001600160401b0397888160b01c1660a089015260f01c60c08801526101208b01968752511615612910578b936127398a92612428565b5f9760208751015115612834575b50505050505060a0905101511690811515908161282b575b506127ae575b50508392917f7ffce3199c8273ece91032d8f943315778e942eff22f8f7d8a7da972073a58b1946127a99251169680519485941684528060208501528301906125d0565b0390a3565b85600d541690813b1561282757885163078d3b7960e01b81525f60048201526001600160a01b03871660248201526001600160401b039190911660448201529082908290606490829084905af1801561281d5715612765576128108291611fae565b61281a5780612765565b80fd5b88513d84823e3d90fd5b8280fd5b9050155f61275f565b8598509461287e9293949551169384159081612901575b828293612867939b80600d5416975116985116915116906125b5565b90156128fa57868060a088510151165b16906125b5565b90803b156102a1578d5163078d3b7960e01b81526001600160a01b0393841660048201529390921660248401526001600160601b031660448301525f908290606490829084905af180156128f0576128dc575b808087818c94612747565b6128e7919450611fae565b5f9260a06128d1565b8b513d5f823e3d90fd5b865f612877565b84518a168e8b1614915061284b565b5050505050505050505050505050565b63ffffffff90818111612931571690565b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608490fd5b519081151582036102a157565b51906001600160601b03821682036102a157565b9291905f946001600160601b0390818316159283801580916131f0575b80156131e3575b6131d3575b8861012097888a0198895160208101511590818015906131c5575b612f7f575b506001600160a01b039a91508a9050612a078c613506565b16808c523303612f73575b6001600160401b0391826009541660a0835101528260a08351015116988c612a3981613603565b99809150151580612f66575b612ef0575b50508590612ee5575b8015612ed8575b612baf575b505050505050612a7e935084604087015116155f14612ba7571661240e565b803410612b7557612a8f903461241b565b9182612a9c575b50505050565b511690804710612b30578280929181925af1612ab66134d7565b5015612ac5575f808080612a96565b60405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269216d73672d76616c756560b01b6044820152606490fd5b50508461240e565b87604083949596979a93510151168681612db0575b8d915080612da5575b612d1f575b88612a7e9b8760808194018051159052606087510163ffffffff815116612d17575b505016918215612cc557505090612c2691604084510152600283515260016020845101528360a084510151169061240e565b96612c308b613603565b50945b612c81575b50505063ffffffff8116151580612c72575b612c58575b88818080612a5f565b81612c66612c6b93836141c9565b6141c9565b5f80612c4f565b5063ffffffff82161515612c4a565b612cb294979350908660a09216604082510152600181515260016020825101528b60c082510152510151169061240e565b92612cbc87613603565b505f8080612c38565b61ffff919792509992991680612cdc575b50612c33565b612d0592999196506003845152600160208551015260c0845101528360a084510151169061240e565b96612d0f8b613603565b50945f612cd6565b528f5f612bf4565b6080919a955001511580159081612d97575b8115612d7c575b5015612d47578b93988b612bd2565b60405162461bcd60e51b815260206004820152600d60248201526c085d1c1cdb0b5a5b9d985b1a59609a1b6044820152606490fd5b905080612d8a575b5f612d38565b5086841687891610612d84565b8589168a8a16119150612d31565b50888b161515612bcd565b612e53575b50878a1680612dc5575b86612bc4565b90945060808c01511580159182612e42575b8215612e20575b505015612ded578b935f612dbf565b60405162461bcd60e51b815260206004820152600b60248201526a085cdb0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612e31575b505f80612dde565b90508760408451015116105f612e29565b8451604001518a1681109250612dd7565b90945060808c01511580159182612ecb575b8215612eae575b505015612e7b578b935f612db5565b60405162461bcd60e51b815260206004820152600b60248201526a085d1c0b5a5b9d985b1a5960aa1b6044820152606490fd5b90915081612ebf575b505f80612e6c565b9050878916105f612eb7565b809250898b161191612e65565b5061ffff81161515612a5a565b50878a161515612a53565b909192939495508c806010541692511690823b156102a15760445f928360405195869485936356b4b2ad60e01b8552600485015260248401525af18015612f5b57612f42575b908c8e95949392612a4a565b612f50919493929d50611fae565b5f9b9091925f612f36565b6040513d5f823e3d90fd5b508d601054161515612a45565b5f608082510152612a12565b51929350909160ff1660031461316f575b50600e546040808c015160608d0151825163052ec72760e51b81523360048201526001600160a01b0392831660248201526001600160b01b0319918216604482015292949093909291839183916064918391165afa9283156118e4575f93613093575b505050866080820151161561306057810151151560808b015115151461301d579081808c936129ef565b5162461bcd60e51b815260206004820152601760248201527f217265647563652d77726f6e672d646972656374696f6e0000000000000000006044820152606490fd5b815162461bcd60e51b815260206004820152600c60248201526b10b73796b837b9b4ba34b7b760a11b6044820152606490fd5b909180935082813d8311613168575b6130ac8183611fc1565b810103126102a15783519283018381106001600160401b03821117611f7f5784526130d6826122a5565b8352602082015190811681036102a15760208301526130f6838201612985565b83830152613106606082016122a5565b606083015261311760808201612992565b608083015260a081015163ffffffff811681036102a15760a083015261313f60c08201612992565b60c083015261315060e08201612992565b60e083015261010080910151908201525f8080612ff3565b503d6130a2565b613180575f60408a5101525f612f90565b60405162461bcd60e51b815260206004820152601a60248201527f21747261696c696e672d73746f702d6d7573742d7265647563650000000000006044820152606490fd5b50600360ff825116146129ea565b5f60206101208a015101526129cf565b5061ffff871615156129ca565b5083861615156129c3565b5f547f6802d0b735cb1927bc8d580cfb1f7e0307b427272c81609ccce7a80ab5afb070602060ff63ffffffff613235818660a01c166122b9565b958187169563ffffffff60a01b8860a01b169063ffffffff60a01b1916175f55610100810190868252865f5260018552610120600360405f2060018060a01b039485855116956001600160601b0360a01b8a87015160a01b1687178355600183019060408701511681546060880151918b60f01b60808a0151151560f01b16926001600160501b0360a01b9060101c16918c60f81b1617171790556001600160601b0360a086015116906001600160601b0360601b60c087015160601b169163ffffffff60c01b60e088015160c01b169163ffffffff60e01b905160e01b16921717176002820155019101926133a98451918683511684549061ff008a860151151560081b166dffffffffffffffffffffffff0000604087015160101b169163ffffffff60701b606088015160701b169371ffffffffffffffffffffffffffffffffffff1916171717178455608083015116839081549063ffffffff60901b9060901b169063ffffffff60901b1916179055565b60a08101519082549060c061ffff60f01b91015160f01b16916001600160401b0360b01b9060b01b169060018060b01b0316171790555f52600283526133f28560405f2061458a565b508051518216613414576134058561453c565b505b515116604051908152a290565b61341d856144e9565b50613407565b6003548110156122f25760035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01905f90565b6005548110156122f25760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001905f90565b80548210156122f2575f5260205f2001905f90565b9060418151145f146134ce576134ca91602082015190606060408401519301515f1a9061446e565b9091565b50505f90600290565b3d15613501573d906134e8826121af565b916134f66040519384611fc1565b82523d5f602084013e565b606090565b335f52600b60205260ff60405f20541661351f57503390565b80516001600160a01b039190821615158061359b575b8061358a575b1561354557511690565b60405162461bcd60e51b815260206004820152601a60248201527f66756e64696e672d6163636f756e742d6f726465722d6661696c0000000000006044820152606490fd5b5060ff61012082015151161561353b565b506020610120820151015115613535565b519060ff821682036102a157565b519061ffff821682036102a157565b156135d057565b60405162461bcd60e51b815260206004820152600b60248201526a216d61782d65787069727960a81b6044820152606490fd5b905f91600460ff610120830151511610156141965761012081015160ff81511660018114801561418c575b156141155750506001600160601b036040610120830151015116156140e7575b600d54604082810151905163185c596360e11b81526001600160a01b03918216600482015291169390606081602481885afa8015612f5b575f9061405f575b60200180516affffffffffffffffffffff925082161561402a5760206101208501510151159182159261400d575b505015613fdc576060820151604051637061696b60e01b81526001600160b01b031990911660048201526101c081602481885afa908115612f5b575f91613e9f575b5061ffff60808201511615613e695763ffffffff606061012085015101511680613de0575b5063ffffffff6080610120850151015116613d75575b63ffffffff61374642612920565b1660e08401526001600160601b0360a08401511661ffff60c083015116026001600160601b0381169081036122ce576001600160601b03612710820411613d215761271090046001600160601b031660c0840152610120830151602001515f9586929091156139a55750508160208401525b6001600160401b0360a061012085015101511690811515908161399c575b5061392a575b50506137e7816131fb565b63ffffffff8116610100830152600160a01b60019003825116600160a01b60019003604084015116916001600160501b0360b01b6060850151169060208501516001600160601b031660a08601516001600160601b03166101208701519060408201516001600160601b031660c08901516001600160601b031660808a01511515845160ff16906020860151151592606087015163ffffffff1694608088015163ffffffff169660a08901516001600160401b03169860c0015161ffff16996040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e08701526101008601526101208501523361014085015261016084015261018083015263ffffffff16906101a07feb2d670c93b7063878c5ca9fe0ec6eb06b845e9e4250f6cf7e0087680422efb291a4610100015163ffffffff169190565b600d546001600160a01b031690813b156128275760405163e4652f4960e01b81525f60048201523360248201526001600160401b0382166044820152918391839160649183915af1801561399157156137dc576139878291611fae565b61281a57806137dc565b6040513d84823e3d90fd5b9050155f6137d6565b909550610160810151613ce6576001600160601b0360208501511615613cb7576001600160601b0360a08501511690670de0b6b3a76400009180830290838204036122ce576001600160601b03602087015116908115613ca3570490828210613c6e576080015161ffff168281029281159184041417156122ce5711613c3957600f546040840151606085015160a08601516001600160601b0316926001600160a01b03928316926001600160b01b03199092169116803b156102a1575f9260649160405195869485936318512afb60e21b85526004850152602484015260448301525afa8015612f5b57613c26575b50613abb6001600160601b036020850151166001600160601b0360c086015116906125b5565b60408401519095906001600160a01b031680613ba25750505060018060a01b03600d54166001600160401b03906001600160601b03613b058360a0610120880151015116886125b5565b1691613b2960018060a01b036040870151169160a0610120880151015116886125b5565b92823b15613b9e5760405163e4652f4960e01b81526001600160a01b039290921660048301523360248301526001600160601b0393909316604482015291839183916064918391905af1801561399157908291613b8a575b505060016137b8565b613b9390611fae565b61281a57805f613b81565b8480fd5b813b15613c225760405163e4652f4960e01b81526001600160a01b039190911660048201523360248201526001600160601b03871660448201529083908290606490829084905af18015613c1757908391613bff575b50506137b8565b613c0890611fae565b613c1357815f613bf8565b5080fd5b6040513d85823e3d90fd5b8380fd5b613c31919250611fae565b5f905f613a95565b60405162461bcd60e51b815260206004820152600d60248201526c216d61782d6c6576657261676560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600d60248201526c216d696e2d6c6576657261676560981b6044820152606490fd5b634e487b7160e01b5f52601260045260245ffd5b60405162461bcd60e51b815260206004820152600760248201526610b6b0b933b4b760c91b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272216d61726b65742d7265647563652d6f6e6c7960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608490fd5b82516001600160a01b03165f9081526002602090815260408083206101208701516080015163ffffffff1684526001019091529020546137385760405162461bcd60e51b815260206004820152600960248201526821757365722d6f636f60b81b6044820152606490fd5b4211613e3457613dff4263ffffffff606061012087015101511661241b565b6101208401515160ff16613e2157613e1b9060075410156135c9565b5f613722565b613e2f9060085410156135c9565b613e1b565b60405162461bcd60e51b815260206004820152600d60248201526c216578706972792d76616c756560981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d216d61726b65742d65786973747360901b6044820152606490fd5b90506101c0813d6101c011613fd4575b81613ebd6101c09383611fc1565b810103126102a157604051906101c082018281106001600160401b03821117611f7f576040528051825260208101516001600160a01b0319811681036102a1576020830152613f0e604082016122a5565b604083015260608101516060830152613f29608082016135ba565b6080830152613f3a60a082016135ba565b60a0830152613f4b60c082016135ba565b60c0830152613f5c60e082016135ba565b60e0830152610100613f6f8183016135ba565b90830152613f8061012082016135ac565b610120830152610140613f948183016135ac565b90830152610160613fa6818301612985565b90830152610180613fb88183016135ba565b90830152613fca6101a08092016135ba565b908201525f6136fd565b3d9150613eaf565b60405162461bcd60e51b8152602060048201526009602482015268216d696e2d73697a6560b81b6044820152606490fd5b9091506001600160601b0360a08501511691511611155f806136bb565b60405162461bcd60e51b815260206004820152600d60248201526c2161737365742d65786973747360981b6044820152606490fd5b506060813d6060116140df575b8161407960609383611fc1565b810103126102a157604051606081018181106001600160401b03821117611f7f576040526140a6826135ac565b81526020820151916affffffffffffffffffffff831683036102a15760406140d59160209485850152016122a5565b604082015261368d565b3d915061406c565b60405162461bcd60e51b815260206004820152600660248201526521707269636560d01b6044820152606490fd5b600314614123575b5061364e565b60c0015161ffff16801515908161417f575b5015614141575f61411d565b60405162461bcd60e51b8152602060048201526016602482015275085d1c985a5b1a5b99cb5cdd1bdc0b5a5b9d985b1a5960521b6044820152606490fd5b6107d0915011155f614135565b506002811461362e565b60405162461bcd60e51b815260206004820152600b60248201526a216f726465722d7479706560a81b6044820152606490fd5b63ffffffff165f908152600160205260409020600301805463ffffffff60901b191660909290921b63ffffffff60901b16919091179055565b805f526004908160205260405f20548015155f146142f7575f19908082018181116142e457600354908382019182116142d157808203614288575b50505060035480156142755781019061425582613423565b909182549160031b1b191690556003555f526020525f6040812055600190565b603184634e487b7160e01b5f525260245ffd5b6142bc6142976142a693613423565b90549060031b1c928392613423565b819391549060031b91821b915f19901b19161790565b90555f528360205260405f20555f808061423d565b601186634e487b7160e01b5f525260245ffd5b601185634e487b7160e01b5f525260245ffd5b5050505f90565b5f8181526006602052604090205480156143b7575f19908082018181116122ce57600554908382019182116122ce57808203614383575b505050600554801561436f5781019061434d82613458565b909182549160031b1b191690556005555f5260066020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b6143a16143926142a693613458565b90549060031b1c928392613458565b90555f52600660205260405f20555f8080614335565b50505f90565b906001820191815f528260205260405f2054908115155f14614466575f19918083018181116122ce578254908482019182116122ce57808203614431575b5050508054801561436f57820191614413838361348d565b909182549160031b1b19169055555f526020525f6040812055600190565b6144516144416142a6938661348d565b90549060031b1c9283928661348d565b90555f528460205260405f20555f80806143fb565b505050505f90565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116144de576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15612f5b575f516001600160a01b038116156144d657905f90565b505f90600190565b505050505f90600390565b805f52600660205260405f2054155f1461453757600554600160401b811015611f7f576145206142a6826001859401600555613458565b9055600554905f52600660205260405f2055600190565b505f90565b805f52600460205260405f2054155f1461453757600354600160401b811015611f7f576145736142a6826001859401600355613423565b9055600354905f52600460205260405f2055600190565b5f8281526001820160205260409020546143b757805490600160401b821015611f7f57826145c26142a684600180960185558461348d565b90558054925f520160205260405f205560019056fea264697066735822122073544964512f140197b410bd1dbd91dfadf04ff2d797ff6ecaaec45306031c5164736f6c63430008160033

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

000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839

-----Decoded View---------------
Arg [0] : _addressStorage (address): 0xB7A842f997cDBD0291D58F0d4Ce729436369C839

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b7a842f997cdbd0291d58f0d4ce729436369c839


Block Transaction Gas Used Reward
view all blocks sequenced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.