ETH Price: $3,385.32 (+6.07%)
Gas: 25 Gwei

Contract

0x01b3A877C100A077124DD21e71969E1b89f2b489
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
184725262023-10-31 20:35:35257 days ago1698784535  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ShortOrdersFacet

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
shanghai EvmVersion
File 1 of 20 : ShortOrdersFacet.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {U256, U88, U80} from "contracts/libraries/PRBMathHelper.sol";

import {Errors} from "contracts/libraries/Errors.sol";
import {Events} from "contracts/libraries/Events.sol";
import {STypes, MTypes, O} from "contracts/libraries/DataTypes.sol";
import {Modifiers} from "contracts/libraries/AppStorage.sol";
import {LibOrders} from "contracts/libraries/LibOrders.sol";
import {LibAsset} from "contracts/libraries/LibAsset.sol";
import {LibOracle} from "contracts/libraries/LibOracle.sol";
import {Constants} from "contracts/libraries/Constants.sol";

// import {console} from "contracts/libraries/console.sol";

contract ShortOrdersFacet is Modifiers {
    using U256 for uint256;
    using U88 for uint88;
    using U80 for uint80;

    /**
     * @notice Creates limit short in market system
     * @dev incomingShort created here instead of AskMatchAlgo to prevent stack too deep
     * @dev Shorts can only be limits
     *
     * @param asset The market that will be impacted
     * @param price Unit price in eth for erc sold
     * @param ercAmount Amount of erc minted and sold
     * @param orderHintArray Array of hint ID for gas-optimized sorted placement on market
     * @param shortHintArray Array of hint ID for gas-optimized short matching above oracle price
     * @param initialCR Initial Collateral Ratio for a short order, between min/max, converted to uint8
     */
    function createLimitShort(
        address asset,
        uint80 price,
        uint88 ercAmount,
        MTypes.OrderHint[] memory orderHintArray,
        uint16[] memory shortHintArray,
        uint16 initialCR
    ) external isNotFrozen(asset) onlyValidAsset(asset) nonReentrant {
        MTypes.CreateLimitShortParam memory p;
        STypes.Asset storage Asset = s.asset[asset];

        uint256 cr = LibOrders.convertCR(initialCR);
        if (Asset.initialCR > initialCR || cr >= Constants.CRATIO_MAX) {
            revert Errors.InvalidInitialCR();
        }

        p.eth = price.mul(ercAmount);
        p.minAskEth = LibAsset.minAskEth(asset);
        p.minShortErc = LibAsset.minShortErc(asset);
        if (ercAmount < p.minShortErc || p.eth < p.minAskEth) {
            revert Errors.OrderUnderMinimumSize();
        }
        // For a short, need enough collateral to cover minting ERC (calculated using initialCR)
        if (s.vaultUser[Asset.vault][msg.sender].ethEscrowed < p.eth.mul(cr)) {
            revert Errors.InsufficientETHEscrowed();
        }

        STypes.Order memory incomingShort;
        incomingShort.addr = msg.sender;
        incomingShort.price = price;
        incomingShort.ercAmount = ercAmount;
        incomingShort.id = Asset.orderIdCounter;
        incomingShort.orderType = O.LimitShort;
        incomingShort.creationTime = LibOrders.getOffsetTime();
        incomingShort.initialCR = initialCR; // 175 -> 1.75x

        p.startingId = s.bids[asset][Constants.HEAD].nextId;

        STypes.Order storage highestBid = s.bids[asset][p.startingId];
        //@dev if match and match price is gt .5% to saved oracle in either direction, update startingShortId
        if (highestBid.price >= incomingShort.price && highestBid.orderType == O.LimitBid)
        {
            LibOrders.updateOracleAndStartingShortViaThreshold(
                asset, LibOracle.getPrice(asset), incomingShort, shortHintArray
            );
        }

        p.oraclePrice = LibOracle.getSavedOrSpotOraclePrice(asset);
        //@dev reading spot oracle price
        if (incomingShort.price < p.oraclePrice) {
            LibOrders.addShort(asset, incomingShort, orderHintArray);
        } else {
            LibOrders.sellMatchAlgo(asset, incomingShort, orderHintArray, p.minAskEth);
        }
    }
}

File 2 of 20 : PRBMathHelper.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {mulDiv as _mulDiv, mulDiv18, UNIT} from "@prb/math/src/Common.sol";
import {Errors} from "contracts/libraries/Errors.sol";

library U256 {
    function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }

    function mulDiv(uint256 x, uint256 y, uint256 denominator)
        internal
        pure
        returns (uint256 result)
    {
        return _mulDiv(x, y, denominator);
    }

    function inv(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is UNIT * UNIT.
            result = 1e36 / x;
        }
    }

    function divU80(uint256 x, uint256 y) internal pure returns (uint80 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint80).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint80(_result);
    }

    function divU64(uint256 x, uint256 y) internal pure returns (uint64 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint64).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint64(_result);
    }

    // test
    function divU88(uint256 x, uint256 y) internal pure returns (uint88 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint88).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint88(_result);
    }
}

// uint128
library Math128 {
    // just passing the result of casting the first param to 256
    function mul(uint128 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function div(uint128 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }
}

// uint104
library Math104 {
    // just passing the result of casting the first param to 256
    function mul(uint104 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function div(uint104 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }
}

// uint96
library U96 {
    // just passing the result of casting the first param to 256
    function mul(uint96 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function div(uint96 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }

    function divU64(uint96 x, uint256 y) internal pure returns (uint64 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint64).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint64(_result);
    }
}

// uint88
library U88 {
    // just passing the result of casting the first param to 256
    function mul(uint88 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function mulU88(uint88 x, uint256 y) internal pure returns (uint88 result) {
        uint256 _result = mulDiv18(x, y);
        if (_result > type(uint88).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint88(_result);
    }

    function div(uint88 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }

    function divU88(uint88 x, uint256 y) internal pure returns (uint88 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint88).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint88(_result);
    }

    function divU80(uint88 x, uint256 y) internal pure returns (uint80 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint80).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint80(_result);
    }
}

// uint80
library U80 {
    // just passing the result of casting the first param to 256
    function mul(uint80 x, uint256 y) internal pure returns (uint256 result) {
        result = mulDiv18(x, y);
    }

    function mulU80(uint80 x, uint256 y) internal pure returns (uint80 result) {
        uint256 _result = mulDiv18(x, y);
        if (_result > type(uint80).max) revert Errors.InvalidPrice(); // assume price?
        result = uint80(_result);
    }

    function mulU88(uint80 x, uint256 y) internal pure returns (uint88 result) {
        uint256 _result = mulDiv18(x, y);
        if (_result > type(uint80).max) revert Errors.InvalidPrice(); // assume price?
        result = uint88(_result);
    }

    function div(uint80 x, uint256 y) internal pure returns (uint256 result) {
        result = _mulDiv(x, UNIT, y);
    }

    // test
    function inv(uint80 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is UNIT * UNIT.
            result = 1e36 / x;
        }
    }
}

File 3 of 20 : Errors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

library Errors {
    error AlreadyMinted();
    error AssetIsFrozen();
    error AssetIsNotPermanentlyFrozen();
    error BadHintIdArray();
    error BadShortHint();
    error BridgeAlreadyCreated();
    error CannotCancelMoreThan1000Orders();
    error CannotExitPartialFillSR();
    error CannotFlagSelf();
    error CannotLeaveDustAmount();
    error CannotLiquidateSelf();
    error CannotMintAnymoreNFTs();
    error CannotMintLastShortRecord();
    error CannotSocializeDebt();
    error CannotTransferFlaggableShort();
    error CannotTransferFlaggedShort();
    error CollateralHigherThanMax();
    error CRLowerThanMin();
    error DifferentVaults();
    error ExitShortPriceTooLow();
    error FirstShortDeleted();
    error FirstShortMustBeNFT();
    error FunctionNotFound(bytes4 _functionSelector);
    error AlreadyLiquidatable();
    error InvalidAmount();
    error InvalidAsset();
    error InvalidBridge();
    error InvalidBuyback();
    error InvalidFlaggerHint();
    error InvalidInitialCR();
    error InvalidMsgValue();
    error InvalidPrice();
    error InvalidShortId();
    error InvalidTithe();
    error InvalidTokenId();
    error InvalidTwapPrice();
    error InvalidTWAPSecondsAgo();
    error InvalidVault();
    error InvalidDeth();
    error InsufficientWalletBalance();
    error InsufficientCollateral();
    error InsufficientERCEscrowed();
    error InsufficientETHEscrowed();
    error InsufficientEthInLiquidityPool();
    error InsufficientNumberOfShorts();
    error IsNotNFT();
    error LiquidationAlreadyFlagged();
    error LiquidationIneligibleWindow();
    error SecondaryLiquidationNoValidShorts();
    error MarketAlreadyCreated();
    error NoDittoReward();
    error NoSells();
    error NoShares();
    error NotActiveOrder();
    error NotBridgeForBaseCollateral();
    error NotDiamond();
    error NotLastOrder();
    error NotMinted();
    error NotOwner();
    error NotOwnerOrAdmin();
    error NotOwnerCandidate();
    error NoYield();
    error OrderIdCountTooLow();
    error OrderUnderMinimumSize();
    error OriginalShortRecordCancelled();
    error ParameterIsZero();
    error PostExitCRLtPreExitCR();
    error PriceOrAmountIs0();
    error ReceiverExceededShortRecordLimit();
    error ReentrantCall();
    error ReentrantCallView();
    error ShortNotFlagged();
    error ShortRecordIdOverflow();
    error ShortRecordIdsNotSorted();
    error SufficientCollateral();
    error TooManyHints();
    error UnderMinimum();
    error UnderMinimumDeposit();
    error VaultAlreadyCreated();

    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);
    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);
    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);
    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);
    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);
}

File 4 of 20 : Events.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {STypes, MTypes, O} from "contracts/libraries/DataTypes.sol";

library Events {
    event CreateShortRecord(address indexed asset, address indexed user, uint16 id);
    event DeleteShortRecord(address indexed asset, address indexed user, uint16 id);
    event CancelOrder(address indexed asset, uint16 id, O indexed orderType);

    event DepositEth(address indexed bridge, address indexed user, uint256 amount);
    event Deposit(address indexed bridge, address indexed user, uint256 amount);
    event UnstakeEth(
        address indexed bridge, address indexed user, uint256 amount, uint256 fee
    );
    event Withdraw(
        address indexed bridge, address indexed user, uint256 amount, uint256 fee
    );
    event WithdrawTapp(address indexed bridge, address indexed recipient, uint256 amount);

    event IncreaseCollateral(
        address indexed asset, address indexed user, uint8 id, uint256 amount
    );
    event DecreaseCollateral(
        address indexed asset, address indexed user, uint8 id, uint256 amount
    );
    event CombineShorts(address indexed asset, address indexed user, uint8[] ids);

    event ExitShortWallet(
        address indexed asset, address indexed user, uint8 id, uint256 amount
    );
    event ExitShortErcEscrowed(
        address indexed asset, address indexed user, uint8 id, uint256 amount
    );
    event ExitShort(
        address indexed asset, address indexed user, uint8 id, uint256 amount
    );

    event MatchOrder(
        address indexed asset,
        address indexed user,
        O indexed orderType,
        uint16 id,
        uint88 fillEth,
        uint88 fillErc
    );

    event CreateOrder(
        address indexed asset,
        address indexed user,
        O indexed orderType,
        uint16 id,
        uint88 ercAmount
    );

    event FlagShort(
        address indexed asset,
        address indexed shorter,
        uint8 id,
        address indexed caller,
        uint256 timestamp
    );
    event Liquidate(
        address indexed asset,
        address indexed shorter,
        uint8 id,
        address indexed caller,
        uint256 amount
    );
    event LiquidateSecondary(
        address indexed asset,
        MTypes.BatchLiquidation[] batches,
        address indexed caller,
        bool isWallet
    );

    event UpdateYield(uint256 indexed vault);
    event DistributeYield(
        uint256 indexed vault,
        address indexed user,
        uint256 yield,
        uint256 dittoYieldShares
    );
    event ClaimDittoMatchedReward(uint256 indexed vault, address indexed user);

    event ShutdownMarket(address indexed asset);
    event RedeemErc(
        address indexed asset, address indexed user, uint256 amtWallet, uint256 amtEscrow
    );

    event CreateMarket(address indexed asset, STypes.Asset assetStruct);
    event ChangeMarketSetting(address indexed asset);
    event CreateVault(address indexed deth, uint256 indexed vault);
    event ChangeVaultSetting(uint256 indexed vault);
    event CreateBridge(address indexed bridge, STypes.Bridge bridgeStruct);
    event ChangeBridgeSetting(address indexed bridge);
    event NewOwnerCandidate(address newOwnerCandidate);
    event NewAdmin(address newAdmin);
    event UpdateAssetOracle(address indexed asset, address newOracle);

    //move to test events / test types
    event FindOrderHintId(uint16 scenario);

    // ERC-721
    event Transfer(address indexed from, address indexed to, uint256 indexed id);
    event Approval(address indexed owner, address indexed spender, uint256 indexed id);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
}

File 5 of 20 : DataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

// import {console} from "contracts/libraries/console.sol";

//@dev leave room for others frozen types
//@dev Asset frozen status
enum F {
    Unfrozen,
    Permanent
}

// @dev if this is changed, modify orderTypetoString in libraries/console.sol
// @dev Order types
enum O {
    Uninitialized,
    LimitBid,
    LimitAsk,
    MarketBid,
    MarketAsk,
    LimitShort,
    Cancelled,
    Matched
}

// @dev ShortRecord status
enum SR {
    PartialFill,
    FullyFilled,
    Closed
}

// @dev oracle frequency
enum OF {
    OneHour,
    FifteenMinutes
}

// 2**n-1 with 18 decimals (prices, amount)
// uint64 = 18.45
// uint72 = 4.722k
// uint80 = 1.2m
// uint88 = 300m
// uint96 = 79B
// uint104 = 1.2t

// DataTypes used in storage
library STypes {
    // 2 slots
    struct Order {
        // SLOT 1: 88 + 80 + 16 + 16 + 16 + 8 + 8 + 16 + 8 = 256
        uint88 ercAmount; // max 300m erc
        uint80 price; // max 1.2m eth
        // max orders 65k, with id re-use
        uint16 prevId;
        uint16 id;
        uint16 nextId;
        O orderType;
        // @dev diff against contract creation timestamp to prevent overflow in 2106
        uint32 creationTime; // seconds
        // SLOT 2: 160 + 32 = 192 (64 unused)
        address addr; // 160
        O prevOrderType;
        // @dev storing as 170 with 2 decimals -> 1.70 ether
        uint16 initialCR; // @dev only used for LimitShort
        uint8 shortRecordId; // @dev only used for LimitShort
        uint64 filler;
    }

    // 2 slots
    // @dev dethYieldRate should match Vault
    struct ShortRecord {
        // SLOT 1: 88 + 88 + 80 = 256
        uint88 collateral; // price * ercAmount * initialCR
        uint88 ercDebt; // same as Order.ercAmount
        uint80 dethYieldRate;
        // SLOT 2: 64 + 40 + 32 + 24 + 8 + 8 + 8 + 8 = 184 (64 remaining)
        SR status;
        uint8 prevId;
        uint8 id;
        uint8 nextId;
        uint64 ercDebtRate; // socialized penalty rate
        uint32 updatedAt; // seconds
        uint32 flaggedAt; // seconds
        uint24 flaggerId;
        uint40 tokenId; // As of 2023, Ethereum had ~2B total tx. Uint40 max value is 1T, which is more than enough
    }

    struct NFT {
        // SLOT 1: 160 + 8 + 8 = 176 (80 unused)
        address owner;
        uint8 assetId;
        uint8 shortRecordId;
    }

    // uint8:  [0-255]
    // uint16: [0-65_535]
    // @dev see testMultiAssetSettings()
    struct Asset {
        // SLOT 1: 104 + 88 + 16 + 16 + 16 + 8 + 8 = 256 (0 unused)
        uint104 ercDebt; // max 20.2T
        uint88 dethCollateral;
        uint16 startingShortId;
        uint16 orderIdCounter; // max is uint16 but need to throw/handle that?
        uint16 initialCR; // 5 ether -> [1-10, 2 decimals]
        F frozen; // 0 or 1
        uint8 vault;
        // SLOT 2 (Liquidation Parameters)
        // 64 + 16*3 + 8*10 = 192 (64 unused)
        uint8 minBidEth; // 10 -> (1 * 10**18 / 10**2) = 0.1 ether
        uint8 minAskEth; // 10 -> (1 * 10**18 / 10**2) = 0.1 ether
        uint16 minShortErc; // 2000 -> (2000 * 10**18) -> 2000 ether
        uint8 minimumCR; // 1.1 ether -> [1-2, 2 decimals]
        uint8 tappFeePct; // 0.025 ether -> [0-2.5%, 3 decimals]
        uint8 callerFeePct; // 0.005 ether -> [0-2.5%, 3 decimals]
        uint8 forcedBidPriceBuffer; // 1.1 ether -> [1-2, 2 decimals]
        uint8 assetId;
        uint64 ercDebtRate; // max 18x, socialized penalty rate
        uint16 primaryLiquidationCR; // 1.5 ether -> [1-5, 2 decimals]
        uint16 secondaryLiquidationCR; // 1.4 ether -> [1-5, 2 decimals]
        uint8 resetLiquidationTime; // 12 hours -> [1-48 hours, 0 decimals]
        uint8 secondLiquidationTime; // 8 hours -> [1-48 hours, 0 decimals]
        uint8 firstLiquidationTime; // 6 hours -> [1-48 hours, 0 decimals]
        uint64 filler; // keep slots distinct
        // SLOT 3 (Chainlink)
        address oracle; // for non-usd asset
    }

    // 3 slots
    // @dev dethYieldRate should match ShortRecord
    struct Vault {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 dethCollateral; // max 309m, 18 decimals
        uint88 dethTotal; // max 309m, 18 decimals
        uint80 dethYieldRate; // onlyUp
        // SLOT 2: 88 + 16 + 16 = 136 (128 unused)
        // tracked for shorter ditto rewards
        uint88 dethCollateralReward; // onlyUp
        uint16 dethTithePercent; // [0-100, 2 decimals]
        uint16 dittoShorterRate; // per unit of dethCollateral
        uint128 filler2;
        // SLOT 3: 128 + 96 + 16 + 16 = 256
        uint128 dittoMatchedShares;
        uint96 dittoMatchedReward; // max 79B, 18 decimals
        uint16 dittoMatchedRate;
        uint16 dittoMatchedTime; // last claim (in days) from STARTING_TIME
    }

    // 1 slots
    struct AssetUser {
        // SLOT 1: 104 + 24 + 32 + 8 = 168 (88 unused)
        uint104 ercEscrowed;
        uint24 g_flaggerId;
        uint32 g_flaggedAt; // represents the most recent flag - in hours
        uint8 shortRecordIdCounter;
        uint96 filler;
    }

    // 1 slots
    struct VaultUser {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 ethEscrowed;
        uint88 dittoMatchedShares;
        uint80 dittoReward; // max 1.2m, 18 decimals
    }

    struct Bridge {
        // SLOT 1: 16 + 8 + 8 = 32 (224 unused)
        uint8 vault;
        uint16 withdrawalFee;
        uint8 unstakeFee;
    }
}

// @dev DataTypes only used in memory
library MTypes {
    struct OrderHint {
        uint16 hintId;
        uint256 creationTime;
    }

    struct BatchLiquidation {
        address shorter;
        uint8 shortId;
    }

    struct Match {
        uint88 fillEth;
        uint88 fillErc;
        uint88 colUsed;
        uint88 dittoMatchedShares;
        // Below used only for bids
        uint88 shortFillEth; // Includes colUsed + fillEth from shorts
        uint96 askFillErc; // Subset of fillErc
        bool ratesQueried; // Save gas when matching shorts
        uint80 dethYieldRate;
        uint64 ercDebtRate;
    }

    struct ExitShort {
        address asset;
        uint256 ercDebt;
        uint88 collateral;
        uint88 ethFilled;
        uint88 ercAmountLeft;
        uint88 ercFilled;
        uint256 beforeExitCR;
    }

    struct CombineShorts {
        bool shortFlagExists;
        uint32 shortUpdatedAt;
    }

    struct PrimaryLiquidation {
        address asset;
        uint256 vault;
        STypes.ShortRecord short;
        address shorter;
        uint256 cRatio;
        uint80 oraclePrice;
        uint256 forcedBidPriceBuffer;
        uint256 ethDebt;
        uint88 ethFilled;
        uint88 ercDebtMatched;
        bool loseCollateral;
        uint256 tappFeePct;
        uint256 callerFeePct;
        uint88 gasFee;
        uint88 totalFee; // gasFee + tappFee + callerFee
        uint256 minimumCR;
    }

    struct SecondaryLiquidation {
        address asset;
        uint256 vault;
        STypes.ShortRecord short;
        address shorter;
        uint256 cRatio;
        uint256 minimumCR;
        uint88 liquidatorCollateral;
        uint256 oraclePrice;
    }

    struct BidMatchAlgo {
        uint16 askId;
        uint16 shortHintId;
        uint16 shortId;
        uint16 prevShortId;
        uint16 firstShortIdBelowOracle;
        uint16 matchedAskId;
        uint16 matchedShortId;
        bool isMovingBack;
        bool isMovingFwd;
        uint256 oraclePrice;
        uint16 dustAskId;
        uint16 dustShortId;
    }

    struct CreateVaultParams {
        uint16 dethTithePercent;
        uint16 dittoMatchedRate;
        uint16 dittoShorterRate;
    }

    struct CreateLimitShortParam {
        address asset;
        uint256 eth;
        uint256 minShortErc;
        uint256 minAskEth;
        uint16 startingId;
        uint256 oraclePrice;
    }
}

File 6 of 20 : AppStorage.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {STypes, F, SR} from "contracts/libraries/DataTypes.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {Constants} from "contracts/libraries/Constants.sol";

// import {console} from "contracts/libraries/console.sol";

struct AppStorage {
    address admin;
    address ownerCandidate;
    address baseOracle;
    uint24 flaggerIdCounter;
    uint40 tokenIdCounter; //NFT - As of 2023, Ethereum had ~2B total tx. Uint40 max value is 1T, which is more than enough for NFTs
    uint8 reentrantStatus;
    // DETH
    mapping(address deth => uint256 vault) dethVault;
    // Bridge
    mapping(address bridge => STypes.Bridge) bridge;
    // Vault
    mapping(uint256 vault => STypes.Vault) vault;
    mapping(uint256 vault => address[]) vaultBridges;
    mapping(uint256 vault => mapping(address account => STypes.VaultUser)) vaultUser;
    // Assets
    mapping(address asset => STypes.Asset) asset;
    mapping(address asset => mapping(address account => STypes.AssetUser)) assetUser;
    // Assets - Orderbook
    mapping(address asset => mapping(uint16 id => STypes.Order)) bids;
    mapping(address asset => mapping(uint16 id => STypes.Order)) asks;
    mapping(address asset => mapping(uint16 id => STypes.Order)) shorts;
    mapping(
        address asset
            => mapping(address account => mapping(uint8 id => STypes.ShortRecord))
        ) shortRecords;
    mapping(uint24 flaggerId => address flagger) flagMapping;
    // ERC721
    mapping(uint256 tokenId => STypes.NFT) nftMapping;
    mapping(uint256 tokenId => address) getApproved;
    mapping(address owner => mapping(address operator => bool)) isApprovedForAll;
    // ERC721 - Assets
    address[] assets;
    mapping(uint256 assetId => address) assetMapping;
    // ERC721 - METADATA STORAGE/LOGIC
    string name;
    string symbol;
}

function appStorage() pure returns (AppStorage storage s) {
    // solhint-disable-next-line no-inline-assembly
    assembly {
        s.slot := 0
    }
}

contract Modifiers {
    AppStorage internal s;

    modifier onlyDAO() {
        LibDiamond.enforceIsContractOwner();
        _;
    }

    modifier onlyAdminOrDAO() {
        if (msg.sender != LibDiamond.contractOwner() && msg.sender != s.admin) {
            revert Errors.NotOwnerOrAdmin();
        }
        _;
    }

    modifier onlyDiamond() {
        if (msg.sender != address(this)) revert Errors.NotDiamond();
        _;
    }

    modifier onlyValidAsset(address asset) {
        if (s.asset[asset].vault == 0) revert Errors.InvalidAsset();
        _;
    }

    modifier isNotFrozen(address asset) {
        if (s.asset[asset].frozen != F.Unfrozen) revert Errors.AssetIsFrozen();
        _;
    }

    modifier isPermanentlyFrozen(address asset) {
        if (s.asset[asset].frozen != F.Permanent) {
            revert Errors.AssetIsNotPermanentlyFrozen();
        }
        _;
    }

    function _onlyValidShortRecord(address asset, address shorter, uint8 id)
        internal
        view
    {
        uint8 maxId = s.assetUser[asset][shorter].shortRecordIdCounter;
        if (id >= maxId) revert Errors.InvalidShortId();
        if (id < Constants.SHORT_STARTING_ID) revert Errors.InvalidShortId();
        if (s.shortRecords[asset][shorter][id].status == SR.Closed) {
            revert Errors.InvalidShortId();
        }
    }

    modifier onlyValidShortRecord(address asset, address shorter, uint8 id) {
        _onlyValidShortRecord(asset, shorter, id);
        _;
    }

    modifier nonReentrant() {
        if (s.reentrantStatus == Constants.ENTERED) revert Errors.ReentrantCall();
        s.reentrantStatus = Constants.ENTERED;
        _;
        s.reentrantStatus = Constants.NOT_ENTERED;
    }

    modifier nonReentrantView() {
        if (s.reentrantStatus == Constants.ENTERED) revert Errors.ReentrantCallView();
        _;
    }

    modifier onlyValidBridge(address bridge) {
        if (s.bridge[bridge].vault == 0) revert Errors.InvalidBridge();
        _;
    }
}

File 7 of 20 : LibOrders.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {U256, Math104, U80, U88} from "contracts/libraries/PRBMathHelper.sol";

import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {STypes, MTypes, O, SR, OF} from "contracts/libraries/DataTypes.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {Events} from "contracts/libraries/Events.sol";
import {LibAsset} from "contracts/libraries/LibAsset.sol";
import {LibOracle} from "contracts/libraries/LibOracle.sol";
import {LibShortRecord} from "contracts/libraries/LibShortRecord.sol";
import {Constants} from "contracts/libraries/Constants.sol";

// import {console} from "contracts/libraries/console.sol";

library LibOrders {
    using LibOracle for address;
    using U256 for uint256;
    using Math104 for uint104;
    using U80 for uint80;
    using U88 for uint88;

    // @dev in seconds
    function getOffsetTime() internal view returns (uint32 timeInSeconds) {
        // shouldn't overflow in 136 years
        return uint32(block.timestamp - Constants.STARTING_TIME); // @dev(safe-cast)
    }

    function convertCR(uint16 cr) internal pure returns (uint256) {
        return (uint256(cr) * 1 ether) / Constants.TWO_DECIMAL_PLACES;
    }

    // For matched token reward
    function increaseSharesOnMatch(
        address asset,
        STypes.Order memory order,
        MTypes.Match memory matchTotal,
        uint88 eth
    ) internal {
        AppStorage storage s = appStorage();

        // @dev use the diff to get more time (2159), to prevent overflow at year 2106
        uint32 timeTillMatch = getOffsetTime() - order.creationTime;
        if (timeTillMatch > Constants.MIN_DURATION) {
            // shares in eth-days
            uint88 shares = eth * (timeTillMatch / 1 days);
            matchTotal.dittoMatchedShares += shares;

            uint256 vault = s.asset[asset].vault;
            s.vaultUser[vault][order.addr].dittoMatchedShares += shares;
        }
    }

    function currentOrders(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset
    ) internal view returns (STypes.Order[] memory) {
        uint16 currentId = orders[asset][Constants.HEAD].nextId;
        uint256 size;

        while (currentId != Constants.TAIL) {
            size++;
            currentId = orders[asset][currentId].nextId;
        }

        STypes.Order[] memory list = new STypes.Order[](size);
        currentId = orders[asset][Constants.HEAD].nextId; // reset currentId

        for (uint256 i = 0; i < size; i++) {
            list[i] = orders[asset][currentId];
            currentId = orders[asset][currentId].nextId;
        }
        return list;
    }

    function isShort(STypes.Order memory order) internal pure returns (bool) {
        return order.orderType == O.LimitShort;
    }

    function addBid(
        address asset,
        STypes.Order memory order,
        MTypes.OrderHint[] memory orderHintArray
    ) internal {
        AppStorage storage s = appStorage();
        uint16 hintId;

        if (order.orderType == O.MarketBid) {
            return;
        }
        uint16 nextId = s.bids[asset][Constants.HEAD].nextId;
        if (order.price > s.bids[asset][nextId].price || nextId == Constants.TAIL) {
            hintId = Constants.HEAD;
        } else {
            hintId = findOrderHintId(s.bids, asset, orderHintArray);
        }

        addOrder(s.bids, asset, order, hintId);

        uint256 vault = s.asset[asset].vault;
        uint88 eth = order.ercAmount.mulU88(order.price);
        s.vaultUser[vault][order.addr].ethEscrowed -= eth;
    }

    function addAsk(
        address asset,
        STypes.Order memory order,
        MTypes.OrderHint[] memory orderHintArray
    ) internal {
        AppStorage storage s = appStorage();
        uint16 hintId;

        if (order.orderType == O.MarketAsk) {
            return;
        }
        uint16 nextId = s.asks[asset][Constants.HEAD].nextId;
        if (order.price < s.asks[asset][nextId].price || nextId == Constants.TAIL) {
            hintId = Constants.HEAD;
        } else {
            hintId = findOrderHintId(s.asks, asset, orderHintArray);
        }
        addOrder(s.asks, asset, order, hintId);

        s.assetUser[asset][order.addr].ercEscrowed -= order.ercAmount;
    }

    /**
     * @notice Add short struct onto market
     *
     * @param asset The market that will be impacted
     * @param order The short struct passed from shortMatchAlgo
     * @param orderHintArray array of Id passed in front end for optimized looping
     */

    function addShort(
        address asset,
        STypes.Order memory order,
        MTypes.OrderHint[] memory orderHintArray
    ) internal {
        AppStorage storage s = appStorage();
        uint16 hintId;
        uint16 nextId = s.shorts[asset][Constants.HEAD].nextId;
        if (order.price < s.shorts[asset][nextId].price || nextId == Constants.TAIL) {
            hintId = Constants.HEAD;
        } else {
            hintId = findOrderHintId(s.shorts, asset, orderHintArray);
        }

        //@dev: Only need to set this when placing incomingShort onto market
        addOrder(s.shorts, asset, order, hintId);
        updateStartingShortIdViaShort(asset, order);
        uint256 vault = s.asset[asset].vault;
        uint88 eth = order.ercAmount.mulU88(order.price).mulU88(
            LibOrders.convertCR(order.initialCR)
        );
        s.vaultUser[vault][order.addr].ethEscrowed -= eth;
    }

    /**
     * @notice Add ask/short struct onto market
     *
     * @param asset The market that will be impacted
     * @param incomingOrder The ask or short struct passed from sellMatchAlgo
     * @param orderHintArray array of Id passed in front end for optimized looping
     */

    function addSellOrder(
        STypes.Order memory incomingOrder,
        address asset,
        MTypes.OrderHint[] memory orderHintArray
    ) private {
        O o = normalizeOrderType(incomingOrder.orderType);
        if (o == O.LimitShort) {
            addShort(asset, incomingOrder, orderHintArray);
        } else if (o == O.LimitAsk) {
            addAsk(asset, incomingOrder, orderHintArray);
        }
    }

    /**
     * @notice Adds order onto market
     * @dev Reuses order ids for gas saving and id recycling
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param incomingOrder Bid, Ask, or Short Order
     * @param hintId Id passed in front end for optimized looping
     */

    // @dev partial addOrder
    function addOrder(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        STypes.Order memory incomingOrder,
        uint16 hintId
    ) private {
        AppStorage storage s = appStorage();
        // hint.prevId <-> hint <-> hint.nextId
        // set links of incoming to hint
        uint16 prevId = findPrevOfIncomingId(orders, asset, incomingOrder, hintId);
        uint16 nextId = orders[asset][prevId].nextId;
        incomingOrder.prevId = prevId;
        incomingOrder.nextId = nextId;
        uint16 id = incomingOrder.id;
        uint16 canceledID = orders[asset][Constants.HEAD].prevId;
        // @dev (ID) is exiting, [ID] is inserted
        // in this case, the protocol is re-using (ID) and moving it to [ID]
        // check if a previously cancelled or matched order exists
        if (canceledID != Constants.HEAD) {
            incomingOrder.prevOrderType = orders[asset][canceledID].orderType;
            // BEFORE: CancelledID <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
            // AFTER1: CancelledID <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
            uint16 prevCanceledID = orders[asset][canceledID].prevId;
            if (prevCanceledID != Constants.HEAD) {
                orders[asset][Constants.HEAD].prevId = prevCanceledID;
            } else {
                // BEFORE: HEAD <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
                // AFTER1: HEAD <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
                orders[asset][Constants.HEAD].prevId = Constants.HEAD;
            }
            // re-use the previous order's id
            id = incomingOrder.id = canceledID;
        } else {
            // BEFORE: HEAD <-> .. <-> PREV <--------------> NEXT
            // AFTER1: HEAD <-> .. <-> PREV <-> (NEW ID) <-> NEXT
            // otherwise just increment to a new order id
            // and the market grows in height/size
            s.asset[asset].orderIdCounter += 1;
        }
        orders[asset][id] = incomingOrder;
        if (nextId != Constants.TAIL) {
            orders[asset][nextId].prevId = incomingOrder.id;
        }

        orders[asset][prevId].nextId = incomingOrder.id;
        emit Events.CreateOrder(
            asset,
            incomingOrder.addr,
            incomingOrder.orderType,
            incomingOrder.id,
            incomingOrder.ercAmount
        );
    }

    /**
     * @notice Verifies that bid id is between two id based on price
     *
     * @param asset The market that will be impacted
     * @param _prevId The first id supposedly preceding the new price
     * @param _newPrice price of prospective order
     * @param _nextId The first id supposedly following the new price
     *
     * @return direction int direction to search (PREV, EXACT, NEXT)
     */

    function verifyBidId(address asset, uint16 _prevId, uint256 _newPrice, uint16 _nextId)
        internal
        view
        returns (int256 direction)
    {
        AppStorage storage s = appStorage();
        //@dev: TAIL can't be prevId because it will always be last item in list
        bool check1 =
            s.bids[asset][_prevId].price >= _newPrice || _prevId == Constants.HEAD;
        bool check2 =
            _newPrice > s.bids[asset][_nextId].price || _nextId == Constants.TAIL;

        if (check1 && check2) {
            return Constants.EXACT;
        } else if (!check1) {
            return Constants.PREV;
        } else if (!check2) {
            return Constants.NEXT;
        }
    }

    /**
     * @notice Verifies that short id is between two id based on price
     *
     * @param asset The market that will be impacted
     * @param _prevId The first id supposedly preceding the new price
     * @param _newPrice price of prospective order
     * @param _nextId The first id supposedly following the new price
     *
     * @return direction int direction to search (PREV, EXACT, NEXT)
     */
    function verifySellId(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 _prevId,
        uint256 _newPrice,
        uint16 _nextId
    ) private view returns (int256 direction) {
        //@dev: TAIL can't be prevId because it will always be last item in list
        bool check1 =
            orders[asset][_prevId].price <= _newPrice || _prevId == Constants.HEAD;

        bool check2 =
            _newPrice < orders[asset][_nextId].price || _nextId == Constants.TAIL;

        if (check1 && check2) {
            return Constants.EXACT;
        } else if (!check1) {
            return Constants.PREV;
        } else if (!check2) {
            return Constants.NEXT;
        }
    }

    /**
     * @notice Handles the reordering of market when order is canceled
     * @dev Reuses order ids for gas saving and id recycling
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param id Id of order
     */

    function cancelOrder(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 id
    ) internal {
        // save this since it may be replaced
        uint16 prevHEAD = orders[asset][Constants.HEAD].prevId;

        // remove the links of ID in the market
        // @dev (ID) is exiting, [ID] is inserted
        // BEFORE: PREV <-> (ID) <-> NEXT
        // AFTER : PREV <----------> NEXT
        orders[asset][orders[asset][id].nextId].prevId = orders[asset][id].prevId;
        orders[asset][orders[asset][id].prevId].nextId = orders[asset][id].nextId;

        // create the links using the other side of the HEAD
        emit Events.CancelOrder(asset, id, orders[asset][id].orderType);
        _reuseOrderIds(orders, asset, id, prevHEAD, O.Cancelled);
    }

    /**
     * @notice moves the matched id to the prev side of HEAD
     * @dev this is how an id gets re-used
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param id ID of most recent matched order
     *
     */
    function matchOrder(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 id
    ) internal {
        uint16 prevHEAD = orders[asset][Constants.HEAD].prevId;
        _reuseOrderIds(orders, asset, id, prevHEAD, O.Matched);
    }

    // shared function for both canceling and order and matching an order
    function _reuseOrderIds(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 id,
        uint16 prevHEAD,
        O cancelledOrMatched
    ) private {
        // matching ID1 and ID2
        // BEFORE: HEAD <- <---------------- HEAD <-> (ID1) <-> (ID2) <-> (ID3) <-> NEXT
        // AFTER1: HEAD <- [ID1] <---------- HEAD <-----------> (ID2) <-> (ID3) <-> NEXT
        // AFTER2: HEAD <- [ID1] <- [ID2] <- HEAD <---------------------> (ID3) <-> NEXT

        // @dev mark as cancelled instead of deleting the order itself
        orders[asset][id].orderType = cancelledOrMatched;
        orders[asset][Constants.HEAD].prevId = id;
        // Move the cancelled ID behind HEAD to re-use it
        // note: C_IDs (cancelled ids) only need to point back (set prevId, can retain nextId)
        // BEFORE: .. C_ID2 <- C_ID1 <--------- HEAD <-> ... [ID]
        // AFTER1: .. C_ID2 <- C_ID1 <- [ID] <- HEAD <-> ...
        if (prevHEAD != Constants.HEAD) {
            orders[asset][id].prevId = prevHEAD;
        } else {
            // if this is the first ID cancelled
            // HEAD.prevId needs to be HEAD
            // and one of the cancelled id.prevID should point to HEAD
            // BEFORE: HEAD <--------- HEAD <-> ... [ID]
            // AFTER1: HEAD <- [ID] <- HEAD <-> ...
            orders[asset][id].prevId = Constants.HEAD;
        }
    }

    /**
     * @notice Helper function for finding the (previous) id so that an incoming
     * @notice order can be placed onto the correct market.
     * @notice Uses hintId if possible, otherwise fallback to traversing the
     * @notice list of orders starting from HEAD
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param incomingOrder the Order to be placed
     * @param hintId Id used to optimize finding the place to insert into ob
     */

    function findPrevOfIncomingId(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        STypes.Order memory incomingOrder,
        uint16 hintId
    ) internal view returns (uint16) {
        uint16 nextId = orders[asset][hintId].nextId;

        // if invalid hint (if the id points to 0 then it's an empty id)
        if (hintId == 0 || nextId == 0) {
            return getOrderId(
                orders,
                asset,
                Constants.NEXT,
                Constants.HEAD,
                incomingOrder.price,
                incomingOrder.orderType
            );
        }

        // check if the hint is valid
        int256 direction = verifyId(
            orders, asset, hintId, incomingOrder.price, nextId, incomingOrder.orderType
        );

        // if its 0, it's correct
        // otherwise it could be off because a tx could of modified state
        // so search in a direction based on price.
        if (direction == Constants.EXACT) {
            return hintId;
        } else if (direction == Constants.NEXT) {
            return getOrderId(
                orders,
                asset,
                Constants.NEXT,
                nextId,
                incomingOrder.price,
                incomingOrder.orderType
            );
        } else {
            uint16 prevId = orders[asset][hintId].prevId;
            return getOrderId(
                orders,
                asset,
                Constants.PREV,
                prevId,
                incomingOrder.price,
                incomingOrder.orderType
            );
        }
    }

    /**
     * @notice Verifies that an id is between two id based on price and orderType
     *
     * @param asset The market that will be impacted
     * @param prevId The first id supposedly preceding the new price
     * @param newPrice price of prospective order
     * @param nextId The first id supposedly following the new price
     * @param orderType order type (bid, ask, short)
     *
     * @return direction int direction to search (PREV, EXACT, NEXT)
     */
    function verifyId(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 prevId,
        uint256 newPrice,
        uint16 nextId,
        O orderType
    ) internal view returns (int256 direction) {
        orderType = normalizeOrderType(orderType);

        if (orderType == O.LimitAsk || orderType == O.LimitShort) {
            return verifySellId(orders, asset, prevId, newPrice, nextId);
        } else if (orderType == O.LimitBid) {
            return verifyBidId(asset, prevId, newPrice, nextId);
        }
    }

    // @dev not used to change state, just which methods to call
    function normalizeOrderType(O o) private pure returns (O newO) {
        if (o == O.LimitBid || o == O.MarketBid) {
            return O.LimitBid;
        } else if (o == O.LimitAsk || o == O.MarketAsk) {
            return O.LimitAsk;
        } else if (o == O.LimitShort) {
            return O.LimitShort;
        }
    }

    /**
     * @notice Helper function for finding and returning id of potential order
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param direction int direction to search (PREV, EXACT, NEXT)
     * @param hintId hint id
     * @param _newPrice price of prospective order used to find the id
     * @param orderType which OrderType to verify
     */

    function getOrderId(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        int256 direction,
        uint16 hintId,
        uint256 _newPrice,
        O orderType
    ) internal view returns (uint16 _hintId) {
        while (true) {
            uint16 nextId = orders[asset][hintId].nextId;

            if (
                verifyId(orders, asset, hintId, _newPrice, nextId, orderType)
                    == Constants.EXACT
            ) {
                return hintId;
            }

            if (direction == Constants.PREV) {
                uint16 prevId = orders[asset][hintId].prevId;
                hintId = prevId;
            } else {
                hintId = nextId;
            }
        }
    }

    /**
     * @notice Helper function for updating the bids mapping when matched
     * @dev More efficient way to remove matched orders. Instead
     * @dev Instead of canceling each one, just wait till the last match and only swap prevId/nextId there, since the rest are gone
     *
     * @param orders The market that will be impacted
     * @param asset The market that will be impacted
     * @param id Most recent matched Bid
     * @param isOrderFullyFilled Boolean to see if full or partial
     */
    function updateBidOrdersOnMatch(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 id,
        bool isOrderFullyFilled
    ) internal {
        // BEFORE: HEAD <-> ... <-> (ID) <-> NEXT
        // AFTER : HEAD <------------------> NEXT
        if (isOrderFullyFilled) {
            _updateOrders(orders, asset, Constants.HEAD, id);
        } else {
            // BEFORE: HEAD <-> ... <-> (ID)
            // AFTER : HEAD <---------> (ID)
            orders[asset][id].prevId = Constants.HEAD;
            orders[asset][Constants.HEAD].nextId = id;
        }
    }

    /**
     * @notice Helper function for updating the asks/shorts mapping when matched by incomingBid
     * @dev firstShortId isn't necessarily HEAD because orders start matching from oracle price
     *
     * @param asset The market that will be impacted
     * @param b Memory based struct passed from BidMatchAlgo
     */
    function updateSellOrdersOnMatch(address asset, MTypes.BidMatchAlgo memory b)
        internal
    {
        AppStorage storage s = appStorage();
        if (b.matchedAskId != 0) {
            _updateOrders(s.asks, asset, Constants.HEAD, b.matchedAskId);
        }

        if (b.matchedShortId != 0) {
            if (!b.isMovingBack && !b.isMovingFwd) {
                //@dev Handles only matching one thing
                //@dev If does not get fully matched, b.matchedShortId == 0 and therefore not hit this block
                _updateOrders(s.shorts, asset, b.prevShortId, b.matchedShortId);
            } else if (!b.isMovingBack && b.isMovingFwd) {
                //@dev Handles moving forward only
                _updateOrders(
                    s.shorts, asset, b.firstShortIdBelowOracle, b.matchedShortId
                );
            } else if (b.isMovingBack && !b.isMovingFwd) {
                //@handles moving backwards only.
                _updateOrders(s.shorts, asset, b.prevShortId, b.shortHintId);
            } else if (b.isMovingBack && b.isMovingFwd) {
                uint16 id = b.prevShortId == b.firstShortIdBelowOracle
                    ? b.shortHintId
                    : b.matchedShortId;
                //@dev Handle going backward and forward
                _updateOrders(s.shorts, asset, b.firstShortIdBelowOracle, id);
            }
        }
    }

    /**
     * @notice Base helper function for updating any kind of orders
     *
     * @param orders the order mapping
     * @param asset The market that will be impacted
     * @param headId Either HEAD or first SHORT with price >= oracle price
     * @param lastMatchedId Most recent matched SHORT in a specific Bid transaction
     */
    function _updateOrders(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        uint16 headId,
        uint16 lastMatchedId
    ) private {
        // BEFORE: FIRST <-> ... <-> (LAST) <-> NEXT
        // AFTER : FIRST <--------------------> NEXT
        uint16 nextAskId = orders[asset][lastMatchedId].nextId;
        if (nextAskId != Constants.TAIL) {
            orders[asset][nextAskId].prevId = headId;
        }
        orders[asset][headId].nextId = nextAskId;
    }

    /**
     * @notice The matching algorithm for asks
     *
     * @param asset The market that will be impacted
     * @param incomingAsk Newly created ask struct
     * @param orderHintArray Id passed in from front end for optimized looping
     * @param minAskEth Minimum ask dust amount
     *
     */

    function sellMatchAlgo(
        address asset,
        STypes.Order memory incomingAsk,
        MTypes.OrderHint[] memory orderHintArray,
        uint256 minAskEth
    ) internal {
        AppStorage storage s = appStorage();
        uint16 startingId = s.bids[asset][Constants.HEAD].nextId;
        STypes.Order storage highestBidInitial = s.bids[asset][startingId];
        if (incomingAsk.price > highestBidInitial.price) {
            if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
                addSellOrder(incomingAsk, asset, orderHintArray);
            }
            return;
        }
        // matching loop starts
        MTypes.Match memory matchTotal;
        while (true) {
            STypes.Order memory highestBid = s.bids[asset][startingId];
            if (incomingAsk.price <= highestBid.price) {
                // Consider ask filled if only dust amount left
                if (incomingAsk.ercAmount.mul(highestBid.price) == 0) {
                    updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
                    incomingAsk.ercAmount = 0;
                    matchIncomingSell(asset, incomingAsk, matchTotal);
                    return;
                }
                matchHighestBid(incomingAsk, highestBid, asset, matchTotal);
                if (incomingAsk.ercAmount > highestBid.ercAmount) {
                    incomingAsk.ercAmount -= highestBid.ercAmount;
                    highestBid.ercAmount = 0;
                    matchOrder(s.bids, asset, highestBid.id);

                    // loop
                    startingId = highestBid.nextId;
                    if (startingId == Constants.TAIL) {
                        incomingAsk.shortRecordId =
                            matchIncomingSell(asset, incomingAsk, matchTotal);

                        if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
                            addSellOrder(incomingAsk, asset, orderHintArray);
                        }
                        s.bids[asset][Constants.HEAD].nextId = Constants.TAIL;
                        return;
                    }
                } else {
                    if (incomingAsk.ercAmount == highestBid.ercAmount) {
                        matchOrder(s.bids, asset, highestBid.id);
                        updateBidOrdersOnMatch(s.bids, asset, highestBid.id, true);
                    } else {
                        highestBid.ercAmount -= incomingAsk.ercAmount;
                        s.bids[asset][highestBid.id].ercAmount = highestBid.ercAmount;
                        updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
                        // Check reduced dust threshold for existing limit orders
                        if (
                            highestBid.ercAmount.mul(highestBid.price)
                                < LibAsset.minBidEth(asset).mul(Constants.DUST_FACTOR)
                        ) {
                            cancelBid(asset, highestBid.id);
                        }
                    }
                    incomingAsk.ercAmount = 0;
                    matchIncomingSell(asset, incomingAsk, matchTotal);
                    return;
                }
            } else {
                updateBidOrdersOnMatch(s.bids, asset, highestBid.id, false);
                incomingAsk.shortRecordId =
                    matchIncomingSell(asset, incomingAsk, matchTotal);

                if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
                    addSellOrder(incomingAsk, asset, orderHintArray);
                }
                return;
            }
        }
    }

    function matchIncomingSell(
        address asset,
        STypes.Order memory incomingOrder,
        MTypes.Match memory matchTotal
    ) private returns (uint8 shortRecordId) {
        O o = normalizeOrderType(incomingOrder.orderType);

        emit Events.MatchOrder(
            asset,
            incomingOrder.addr,
            incomingOrder.orderType,
            incomingOrder.id,
            matchTotal.fillEth,
            matchTotal.fillErc
        );
        if (o == O.LimitShort) {
            return matchIncomingShort(asset, incomingOrder, matchTotal);
        } else if (o == O.LimitAsk) {
            matchIncomingAsk(asset, incomingOrder, matchTotal);
        }
    }

    /**
     * @notice Final settlement of incoming ask
     *
     * @param asset The market that will be impacted
     * @param incomingAsk Newly created ask struct
     * @param matchTotal Struct of the running matched totals
     */

    function matchIncomingAsk(
        address asset,
        STypes.Order memory incomingAsk,
        MTypes.Match memory matchTotal
    ) private {
        AppStorage storage s = appStorage();
        uint256 vault = s.asset[asset].vault;
        s.assetUser[asset][incomingAsk.addr].ercEscrowed -= matchTotal.fillErc;
        s.vaultUser[vault][incomingAsk.addr].ethEscrowed += matchTotal.fillEth;
        s.vault[vault].dittoMatchedShares += matchTotal.dittoMatchedShares;
    }

    /**
     * @notice Final settlement of incoming short
     *
     * @param asset The market that will be impacted
     * @param incomingShort Newly created short struct
     * @param matchTotal Struct of the running matched totals
     */

    function matchIncomingShort(
        address asset,
        STypes.Order memory incomingShort,
        MTypes.Match memory matchTotal
    ) private returns (uint8 shortRecordId) {
        AppStorage storage s = appStorage();
        STypes.Asset storage Asset = s.asset[asset];
        uint256 vault = Asset.vault;
        STypes.Vault storage Vault = s.vault[vault];

        s.vaultUser[vault][incomingShort.addr].ethEscrowed -= matchTotal.colUsed;
        matchTotal.fillEth += matchTotal.colUsed;

        SR status = incomingShort.ercAmount == 0 ? SR.FullyFilled : SR.PartialFill;

        shortRecordId = LibShortRecord.createShortRecord(
            asset,
            incomingShort.addr,
            status,
            matchTotal.fillEth,
            matchTotal.fillErc,
            Asset.ercDebtRate,
            Vault.dethYieldRate,
            0
        );

        Vault.dittoMatchedShares += matchTotal.dittoMatchedShares;
        Vault.dethCollateral += matchTotal.fillEth;
        Asset.dethCollateral += matchTotal.fillEth;
        Asset.ercDebt += matchTotal.fillErc;
    }

    /**
     * @notice Settles highest bid and updates incoming Ask or Short
     * @dev DittoMatchedShares only assigned for bids sitting > 2 weeks of seconds
     *
     * @param incomingSell Newly created Ask or Short
     * @param highestBid Highest bid (first bid) in the sorted bid
     * @param asset The market that will be impacted
     * @param matchTotal Struct of the running matched totals
     */

    function matchHighestBid(
        STypes.Order memory incomingSell,
        STypes.Order memory highestBid,
        address asset,
        MTypes.Match memory matchTotal
    ) internal {
        AppStorage storage s = appStorage();
        uint88 fillErc = incomingSell.ercAmount > highestBid.ercAmount
            ? highestBid.ercAmount
            : incomingSell.ercAmount;
        uint88 fillEth = highestBid.price.mulU88(fillErc);

        increaseSharesOnMatch(asset, highestBid, matchTotal, fillEth);

        if (incomingSell.orderType == O.LimitShort) {
            matchTotal.colUsed += incomingSell.price.mulU88(fillErc).mulU88(
                LibOrders.convertCR(incomingSell.initialCR)
            );
        }
        matchTotal.fillErc += fillErc;
        matchTotal.fillEth += fillEth;

        // @dev this happens at the end so fillErc isn't affected in previous calculations
        s.assetUser[asset][highestBid.addr].ercEscrowed += fillErc;
    }

    function _updateOracleAndStartingShort(address asset, uint16[] memory shortHintArray)
        private
    {
        AppStorage storage s = appStorage();
        uint256 oraclePrice = LibOracle.getOraclePrice(asset);
        uint256 savedPrice = asset.getPrice();
        asset.setPriceAndTime(oraclePrice, getOffsetTime());
        bool shortOrdersIsEmpty = s.shorts[asset][Constants.HEAD].nextId == Constants.TAIL;
        if (shortOrdersIsEmpty) {
            s.asset[asset].startingShortId = Constants.HEAD;
        } else {
            if (oraclePrice == savedPrice) {
                return;
            }
            uint16 shortHintId;
            for (uint256 i = 0; i < shortHintArray.length;) {
                shortHintId = shortHintArray[i];
                unchecked {
                    ++i;
                }

                {
                    O shortOrderType = s.shorts[asset][shortHintId].orderType;
                    if (
                        shortOrderType == O.Cancelled || shortOrderType == O.Matched
                            || shortOrderType == O.Uninitialized
                    ) {
                        continue;
                    }
                }

                address _asset = asset;
                uint80 shortPrice = s.shorts[_asset][shortHintId].price;
                uint16 prevId = s.shorts[_asset][shortHintId].prevId;
                uint80 prevShortPrice = s.shorts[_asset][prevId].price;
                // @dev force hint to be within 0.5% of oraclePrice
                bool startingShortWithinOracleRange = shortPrice
                    <= oraclePrice.mul(1.005 ether) && prevShortPrice >= oraclePrice;
                bool isExactStartingShort =
                    shortPrice >= oraclePrice && prevShortPrice < oraclePrice;
                bool allShortUnderOraclePrice = shortPrice < oraclePrice
                    && s.shorts[_asset][shortHintId].nextId == Constants.TAIL;

                if (isExactStartingShort) {
                    s.asset[_asset].startingShortId = shortHintId;
                    return;
                } else if (startingShortWithinOracleRange) {
                    // @dev prevShortPrice >= oraclePrice
                    s.asset[_asset].startingShortId = prevId;
                    return;
                } else if (allShortUnderOraclePrice) {
                    s.asset[_asset].startingShortId = Constants.HEAD;
                    return;
                }
            }

            revert Errors.BadShortHint();
        }
    }

    //@dev Update on match if order matches and price diff between order price and oracle > chainlink threshold (i.e. eth .5%)
    function updateOracleAndStartingShortViaThreshold(
        address asset,
        uint256 oraclePrice,
        STypes.Order memory incomingOrder,
        uint16[] memory shortHintArray
    ) internal {
        bool orderPriceGtThreshold;
        //@dev handle .5% deviations in either directions
        if (incomingOrder.price >= oraclePrice) {
            orderPriceGtThreshold =
                (incomingOrder.price - oraclePrice).div(oraclePrice) > 0.005 ether;
        } else {
            orderPriceGtThreshold =
                (oraclePrice - incomingOrder.price).div(oraclePrice) > 0.005 ether;
        }

        if (orderPriceGtThreshold) {
            _updateOracleAndStartingShort(asset, shortHintArray);
        }
    }

    //@dev Possible for this function to never get called if updateOracleAndStartingShortViaThreshold() gets called often enough
    function updateOracleAndStartingShortViaTimeBidOnly(
        address asset,
        OF oracleFrequency,
        uint16[] memory shortHintArray
    ) internal {
        uint256 timeDiff = getOffsetTime() - LibOracle.getTime(asset);
        bool oneHourUpdate = oracleFrequency == OF.OneHour && timeDiff >= 1 hours;
        bool fifteenMinuteUpdate =
            oracleFrequency == OF.FifteenMinutes && timeDiff >= 15 minutes;
        if (oneHourUpdate || fifteenMinuteUpdate) {
            _updateOracleAndStartingShort(asset, shortHintArray);
        }
    }

    function updateStartingShortIdViaShort(
        address asset,
        STypes.Order memory incomingShort
    ) internal {
        AppStorage storage s = appStorage();
        STypes.Asset storage Asset = s.asset[asset];
        uint256 oraclePrice = LibOracle.getPrice(asset);
        uint256 startingShortPrice = s.shorts[asset][Asset.startingShortId].price;
        bool shortOrdersIsEmpty = s.shorts[asset][Constants.HEAD].nextId == Constants.TAIL;

        if (shortOrdersIsEmpty || Asset.startingShortId == Constants.HEAD) {
            if (incomingShort.price >= oraclePrice) {
                Asset.startingShortId = incomingShort.id;
            }
        } else if (
            incomingShort.price < startingShortPrice && incomingShort.price >= oraclePrice
        ) {
            Asset.startingShortId = incomingShort.id;
        }
    }

    function findOrderHintId(
        mapping(address => mapping(uint16 => STypes.Order)) storage orders,
        address asset,
        MTypes.OrderHint[] memory orderHintArray
    ) internal view returns (uint16 hintId) {
        bool anyOrderHintPrevMatched;
        for (uint256 i; i < orderHintArray.length; i++) {
            MTypes.OrderHint memory orderHint = orderHintArray[i];
            STypes.Order storage order = orders[asset][orderHint.hintId];
            O hintOrderType = order.orderType;
            if (hintOrderType == O.Cancelled || hintOrderType == O.Matched) {
                continue;
            } else if (order.creationTime == orderHint.creationTime) {
                return orderHint.hintId;
            } else if (!anyOrderHintPrevMatched && order.prevOrderType == O.Matched) {
                anyOrderHintPrevMatched = true;
            }
        }

        if (anyOrderHintPrevMatched) {
            //@dev If hint was prev matched, assume that hint was close to HEAD and therefore is reasonable to use HEAD
            return Constants.HEAD;
        }

        revert Errors.BadHintIdArray();
    }

    // Helper Functions for cancelling orders
    function cancelBid(address asset, uint16 id) internal {
        AppStorage storage s = appStorage();
        STypes.Order storage bid = s.bids[asset][id];

        O orderType = bid.orderType;
        if (orderType == O.Cancelled || orderType == O.Matched) {
            revert Errors.NotActiveOrder();
        }

        uint256 vault = s.asset[asset].vault;
        uint88 eth = bid.ercAmount.mulU88(bid.price);
        s.vaultUser[vault][bid.addr].ethEscrowed += eth;

        cancelOrder(s.bids, asset, id);
    }

    function cancelAsk(address asset, uint16 id) internal {
        AppStorage storage s = appStorage();
        STypes.Order storage ask = s.asks[asset][id];

        O orderType = ask.orderType;
        if (orderType == O.Cancelled || orderType == O.Matched) {
            revert Errors.NotActiveOrder();
        }

        s.assetUser[asset][ask.addr].ercEscrowed += ask.ercAmount;

        cancelOrder(s.asks, asset, id);
    }

    function cancelShort(address asset, uint16 id) internal {
        AppStorage storage s = appStorage();
        STypes.Order storage short = s.shorts[asset][id];

        O orderType = short.orderType;
        if (orderType == O.Cancelled || orderType == O.Matched) {
            revert Errors.NotActiveOrder();
        }

        STypes.Asset storage Asset = s.asset[asset];
        uint88 eth = short.ercAmount.mulU88(short.price).mulU88(
            LibOrders.convertCR(short.initialCR)
        );
        s.vaultUser[Asset.vault][short.addr].ethEscrowed += eth;

        // Update ShortRecord if exists
        uint8 shortRecordId = short.shortRecordId;
        if (shortRecordId >= Constants.SHORT_STARTING_ID) {
            STypes.ShortRecord storage shortRecord =
                s.shortRecords[asset][short.addr][shortRecordId];
            if (shortRecord.status == SR.Closed) {
                LibShortRecord.deleteShortRecord(asset, short.addr, shortRecordId);
            } else {
                shortRecord.status = SR.FullyFilled;
            }
        }

        // Approximating the startingShortId, rather than expecting exact match
        if (id == Asset.startingShortId) {
            uint256 oraclePrice = LibOracle.getPrice(asset);
            uint256 prevPrice = s.shorts[asset][short.prevId].price;
            if (prevPrice >= oraclePrice) {
                Asset.startingShortId = short.prevId;
            } else {
                Asset.startingShortId = short.nextId;
            }
        }

        cancelOrder(s.shorts, asset, id);
    }
}

File 8 of 20 : LibAsset.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {Constants} from "contracts/libraries/Constants.sol";
import {IAsset} from "interfaces/IAsset.sol";
import {Errors} from "contracts/libraries/Errors.sol";

library LibAsset {
    // @dev used in ExitShortWallet and MarketShutDown
    function burnMsgSenderDebt(address asset, uint88 debt) internal {
        IAsset tokenContract = IAsset(asset);
        uint256 walletBalance = tokenContract.balanceOf(msg.sender);
        if (walletBalance < debt) revert Errors.InsufficientWalletBalance();
        tokenContract.burnFrom(msg.sender, debt);
        assert(tokenContract.balanceOf(msg.sender) < walletBalance);
    }

    // default of 1.7 ether, stored in uint16 as 170
    // range of [1-10],
    // 2 decimal places, divide by 100
    // i.e. 123 -> 1.23 ether
    // @dev cRatio that a short order has to begin at
    function initialCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return
            (uint256(s.asset[asset].initialCR) * 1 ether) / Constants.TWO_DECIMAL_PLACES;
    }

    // default of 1.5 ether, stored in uint16 as 150
    // range of [1-5],
    // 2 decimal places, divide by 100
    // i.e. 120 -> 1.2 ether
    // less than initialCR
    // @dev cRatio that a shortRecord can be liquidated at
    function primaryLiquidationCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].primaryLiquidationCR) * 1 ether)
            / Constants.TWO_DECIMAL_PLACES;
    }

    // default of 1.4 ether, stored in uint16 as 140
    // range of [1-5],
    // 2 decimal places, divide by 100
    // i.e. 120 -> 1.2 ether
    // @dev cRatio that allows for secondary liquidations to happen
    // @dev via wallet or ercEscrowed (vault deposited usd)
    function secondaryLiquidationCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].secondaryLiquidationCR) * 1 ether)
            / Constants.TWO_DECIMAL_PLACES;
    }

    // default of 1.1 ether, stored in uint8 as 110
    // range of [1-2],
    // 2 decimal places, divide by 100
    // i.e. 120 -> 1.2 ether
    // less than primaryLiquidationCR
    // @dev buffer/slippage for forcedBid price
    function forcedBidPriceBuffer(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].forcedBidPriceBuffer) * 1 ether)
            / Constants.TWO_DECIMAL_PLACES;
    }

    // default of 1.1 ether, stored in uint8 as 110
    // range of [1-2],
    // 2 decimal places, divide by 100
    // i.e. 120 -> 1.2 ether
    // @dev cRatio where a shorter loses all collateral on liquidation
    function minimumCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return
            (uint256(s.asset[asset].minimumCR) * 1 ether) / Constants.TWO_DECIMAL_PLACES;
    }

    // default of .025 ether, stored in uint8 as 25
    // range of [0.1-2.5%],
    // 3 decimal places, divide by 1000
    // i.e. 1234 -> 1.234 ether
    // @dev percentage of fees given to TAPP during liquidations
    function tappFeePct(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].tappFeePct) * 1 ether)
            / Constants.THREE_DECIMAL_PLACES;
    }

    // default of .005 ether, stored in uint8 as 5
    // range of [0.1-2.5%],
    // 3 decimal places, divide by 1000
    // i.e. 1234 -> 1.234 ether
    // @dev percentage of fees given to the liquidator during liquidations
    function callerFeePct(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].callerFeePct) * 1 ether)
            / Constants.THREE_DECIMAL_PLACES;
    }

    // default of .1 ether, stored in uint8 as 10
    // range of [.01 - 2.55],
    // 2 decimal places, divide by 100
    // i.e. 125 -> 1.25 ether
    // @dev dust amount
    function minBidEth(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return
            (uint256(s.asset[asset].minBidEth) * 1 ether) / Constants.TWO_DECIMAL_PLACES;
    }

    // default of .1 ether, stored in uint8 as 10
    // range of [.01 - 2.55],
    // 2 decimal places, divide by 100
    // i.e. 125 -> 1.25 ether
    // @dev dust amount
    function minAskEth(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return
            (uint256(s.asset[asset].minAskEth) * 1 ether) / Constants.TWO_DECIMAL_PLACES;
    }

    // default of 2000 ether, stored in uint16 as 2000
    // range of [1 - 65,535 (uint16 max)],
    // i.e. 2000 -> 2000 ether
    // @dev min short record debt
    function minShortErc(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return uint256(s.asset[asset].minShortErc) * 1 ether;
    }

    // default of 6 hours, stored in uint8 as 6
    // range of [1 - 48],
    // i.e. 6 -> 6 hours
    // @dev primary liquidation first eligibility window
    function firstLiquidationTime(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return uint256(s.asset[asset].firstLiquidationTime) * 1 hours;
    }

    // default of 8 hours, stored in uint8 as 8
    // range of [1 - 48],
    // i.e. 8 -> 8 hours
    // @dev primary liquidation second eligibility window
    function secondLiquidationTime(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return uint256(s.asset[asset].secondLiquidationTime) * 1 hours;
    }

    // default of 12 hours, stored in uint8 as 12
    // range of [1 - 48],
    // i.e. 12 -> 12 hours
    // @dev primary liquidation time limit
    function resetLiquidationTime(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return uint256(s.asset[asset].resetLiquidationTime) * 1 hours;
    }
}

File 9 of 20 : LibOracle.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {U256} from "contracts/libraries/PRBMathHelper.sol";

import {AggregatorV3Interface} from
    "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IDiamond} from "interfaces/IDiamond.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {Constants} from "contracts/libraries/Constants.sol";
import {LibOrders} from "contracts/libraries/LibOrders.sol";
import {Errors} from "contracts/libraries/Errors.sol";

// import {console} from "contracts/libraries/console.sol";

library LibOracle {
    using U256 for uint256;

    function getOraclePrice(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        AggregatorV3Interface baseOracle = AggregatorV3Interface(s.baseOracle);
        uint256 protocolPrice = getPrice(asset);
        // prettier-ignore
        (
            uint80 baseRoundID,
            int256 basePrice,
            /*uint256 baseStartedAt*/
            ,
            uint256 baseTimeStamp,
            /*uint80 baseAnsweredInRound*/
        ) = baseOracle.latestRoundData();

        AggregatorV3Interface oracle = AggregatorV3Interface(s.asset[asset].oracle);
        if (address(oracle) == address(0)) revert Errors.InvalidAsset();

        if (oracle == baseOracle) {
            //@dev multiply base oracle by 10**10 to give it 18 decimals of precision
            uint256 basePriceInEth = basePrice > 0
                ? uint256(basePrice * Constants.BASE_ORACLE_DECIMALS).inv()
                : 0;
            basePriceInEth = baseOracleCircuitBreaker(
                protocolPrice, baseRoundID, basePrice, baseTimeStamp, basePriceInEth
            );
            return basePriceInEth;
        } else {
            // prettier-ignore
            (
                uint80 roundID,
                int256 price,
                /*uint256 startedAt*/
                ,
                uint256 timeStamp,
                /*uint80 answeredInRound*/
            ) = oracle.latestRoundData();
            uint256 priceInEth = uint256(price).div(uint256(basePrice));
            oracleCircuitBreaker(
                roundID, baseRoundID, price, basePrice, timeStamp, baseTimeStamp
            );
            return priceInEth;
        }
    }

    function baseOracleCircuitBreaker(
        uint256 protocolPrice,
        uint80 roundId,
        int256 chainlinkPrice,
        uint256 timeStamp,
        uint256 chainlinkPriceInEth
    ) private view returns (uint256 _protocolPrice) {
        bool invalidFetchData = roundId == 0 || timeStamp == 0
            || timeStamp > block.timestamp || chainlinkPrice <= 0
            || block.timestamp > 2 hours + timeStamp;
        uint256 chainlinkDiff = chainlinkPriceInEth > protocolPrice
            ? chainlinkPriceInEth - protocolPrice
            : protocolPrice - chainlinkPriceInEth;
        bool priceDeviation =
            protocolPrice > 0 && chainlinkDiff.div(protocolPrice) > 0.5 ether;

        //@dev if there is issue with chainlink, get twap price. Compare twap and chainlink
        if (invalidFetchData || priceDeviation) {
            uint256 twapPrice = IDiamond(payable(address(this))).estimateWETHInUSDC(
                Constants.UNISWAP_WETH_BASE_AMT, 30 minutes
            );
            uint256 twapPriceInEther = twapPrice * 1 ether / Constants.DECIMAL_USDC;
            uint256 twapPriceInv = twapPriceInEther.inv();
            if (twapPriceInEther == 0) {
                revert Errors.InvalidTwapPrice();
            }

            if (invalidFetchData) {
                return twapPriceInv;
            } else {
                uint256 twapDiff = twapPriceInv > protocolPrice
                    ? twapPriceInv - protocolPrice
                    : protocolPrice - twapPriceInv;
                //@dev save the price that is closest to saved oracle price
                if (chainlinkDiff <= twapDiff) {
                    return chainlinkPriceInEth;
                }
                //@dev In case USDC_WETH suddenly has no liquidity
                IERC20 weth = IERC20(Constants.WETH);
                uint256 wethBal = weth.balanceOf(Constants.USDC_WETH);
                if (wethBal < 100 ether) revert Errors.InsufficientEthInLiquidityPool();
                return twapPriceInv;
            }
        } else {
            return chainlinkPriceInEth;
        }
    }

    function oracleCircuitBreaker(
        uint80 roundId,
        uint80 baseRoundId,
        int256 chainlinkPrice,
        int256 baseChainlinkPrice,
        uint256 timeStamp,
        uint256 baseTimeStamp
    ) private view {
        bool invalidFetchData = roundId == 0 || timeStamp == 0
            || timeStamp > block.timestamp || chainlinkPrice <= 0 || baseRoundId == 0
            || baseTimeStamp == 0 || baseTimeStamp > block.timestamp
            || baseChainlinkPrice <= 0;

        if (invalidFetchData) revert Errors.InvalidPrice();
    }

    /* 
    @dev Constants.HEAD to marks the start/end of the linked list, so the only properties needed are id/nextId/prevId.
    Helper methods are used to set the values of oraclePrice and oracleTime since they are set to different properties
    */
    function setPriceAndTime(address asset, uint256 oraclePrice, uint32 oracleTime)
        internal
    {
        AppStorage storage s = appStorage();
        s.bids[asset][Constants.HEAD].ercAmount = uint80(oraclePrice);
        s.bids[asset][Constants.HEAD].creationTime = oracleTime;
    }

    //@dev Intentionally using creationTime for oracleTime.
    function getTime(address asset) internal view returns (uint256 creationTime) {
        AppStorage storage s = appStorage();
        return s.bids[asset][Constants.HEAD].creationTime;
    }

    //@dev Intentionally using ercAmount for oraclePrice. Storing as price may lead to bugs in the match algos.
    function getPrice(address asset) internal view returns (uint80 oraclePrice) {
        AppStorage storage s = appStorage();
        return uint80(s.bids[asset][Constants.HEAD].ercAmount);
    }

    //@dev allows caller to save gas since reading spot price costs ~16K
    function getSavedOrSpotOraclePrice(address asset) internal view returns (uint256) {
        if (LibOrders.getOffsetTime() - getTime(asset) < 15 minutes) {
            return getPrice(asset);
        } else {
            return getOraclePrice(asset);
        }
    }
}

File 10 of 20 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

library Constants {
    // @dev mark start of orders mapping
    uint8 internal constant HEAD = 1;
    // @dev only used as an alias since it's the same id
    uint8 internal constant TAIL = 1;
    // for all order types, starting point of orders
    uint8 internal constant STARTING_ID = 100;
    uint8 internal constant SHORT_MAX_ID = 254; // max uint8
    uint8 internal constant SHORT_STARTING_ID = 2;

    uint256 internal constant DUST_FACTOR = 0.5 ether;
    uint256 internal constant MIN_DURATION = 14 days;
    uint256 internal constant CRATIO_MAX = 15 ether;
    uint256 internal constant YIELD_DELAY_SECONDS = 60; // just need enough to prevent flash loan
    uint256 internal constant BRIDGE_YIELD_UPDATE_THRESHOLD = 1000 ether;
    uint256 internal constant BRIDGE_YIELD_PERCENT_THRESHOLD = 0.01 ether; // 1%

    // Bridge
    // @dev Matching RocketPool min deposit for now, Lido is 100 wei
    uint88 internal constant MIN_DEPOSIT = 0.01 ether;

    // re-entrancy
    uint8 internal constant NOT_ENTERED = 1;
    uint8 internal constant ENTERED = 2;
    uint256 internal constant ONE_DECIMAL_PLACES = 10;
    uint256 internal constant TWO_DECIMAL_PLACES = 100;
    uint256 internal constant THREE_DECIMAL_PLACES = 1000;
    uint256 internal constant FOUR_DECIMAL_PLACES = 10000;
    uint256 internal constant FIVE_DECIMAL_PLACES = 100000;
    uint256 internal constant SIX_DECIMAL_PLACES = 1000000;

    // set this to a datetime closer to deployment
    // @dev changing this will likely break the end to end fork test
    uint256 internal constant STARTING_TIME = 1660353637;

    int256 internal constant PREV = -1;
    int256 internal constant EXACT = 0;
    int256 internal constant NEXT = 1;

    bool internal constant MARKET_ORDER = true;
    bool internal constant LIMIT_ORDER = false;

    // Oracle
    // Base Oracle needs to be adjust 10**10 to have full 18 precision
    int256 internal constant BASE_ORACLE_DECIMALS = 10 ** 10;

    // Mainnet TWAP
    address internal constant USDC_WETH =
        address(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
    address internal constant USDC = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    address internal constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    uint128 internal constant UNISWAP_WETH_BASE_AMT = 1 ether;
    uint256 internal constant DECIMAL_USDC = 10 ** 6; //USDC's ERC contract sets to 6 decimals
}

library Vault {
    // ONE is the default vault
    uint256 internal constant ONE = 1;
}

File 11 of 20 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 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 ("memory-safe") {
        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) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

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

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

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

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

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

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

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

        // 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;
    }
}

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, 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 + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}

File 12 of 20 : LibDiamond.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/**
 * \
 * Author: Nick Mudge
 *
 * Implementation of Diamond facet.
 * Uses the diamond-2 version 1.3.4 implementation:
 * https://github.com/mudgen/diamond-2
 *
 * This is gas optimized by reducing storage reads and storage writes.
 * This code is as complex as it is to reduce gas costs.
 * /*****************************************************************************
 */

import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol";

/* solhint-disable */
library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION =
        keccak256("diamond.standard.diamond.storage");

    struct DiamondStorage {
        // maps function selectors to the facets that execute the functions.
        // and maps the selectors to their position in the selectorSlots array.
        // func selector => address facet, selector position
        mapping(bytes4 => bytes32) facets;
        // array of slots of function selectors.
        // each slot holds 8 function selectors.
        mapping(uint256 => bytes32) selectorSlots;
        // owner of the contract
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // The number of function selectors in selectorSlots
        uint16 selectorCount;
        // owner of the contract
        address contractOwner;
    }

    function diamondStorage() internal pure returns (DiamondStorage storage ds) {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function setContractOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.contractOwner;
        ds.contractOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function contractOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().contractOwner;
    }

    function enforceIsContractOwner() internal view {
        require(
            msg.sender == diamondStorage().contractOwner,
            "LibDiamond: Must be contract owner"
        );
    }

    event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

    bytes32 constant CLEAR_ADDRESS_MASK = bytes32(uint256(0xffffffffffffffffffffffff));
    bytes32 constant CLEAR_SELECTOR_MASK = bytes32(uint256(0xffffffff << 224));

    // Internal function version of diamondCut
    // This code is almost the same as the external diamondCut,
    // except it is using 'Facet[] memory _diamondCut' instead of
    // 'Facet[] calldata _diamondCut'.
    // The code is duplicated to prevent copying calldata to memory which
    // causes an error for a two dimensional array.
    function diamondCut(
        IDiamondCut.FacetCut[] memory _diamondCut,
        address _init,
        bytes memory _calldata
    ) internal {
        DiamondStorage storage ds = diamondStorage();
        uint256 originalSelectorCount = ds.selectorCount;
        uint256 selectorCount = originalSelectorCount;
        bytes32 selectorSlot;
        // Check if last selector slot is not full
        if (selectorCount % 8 > 0) {
            // get last selectorSlot
            selectorSlot = ds.selectorSlots[selectorCount / 8];
        }
        // loop through diamond cut
        for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
            (selectorCount, selectorSlot) = addReplaceRemoveFacetSelectors(
                selectorCount,
                selectorSlot,
                _diamondCut[facetIndex].facetAddress,
                _diamondCut[facetIndex].action,
                _diamondCut[facetIndex].functionSelectors
            );
        }
        if (selectorCount != originalSelectorCount) {
            ds.selectorCount = uint16(selectorCount);
        }
        // If last selector slot is not full
        if (selectorCount % 8 > 0) {
            ds.selectorSlots[selectorCount / 8] = selectorSlot;
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addReplaceRemoveFacetSelectors(
        uint256 _selectorCount,
        bytes32 _selectorSlot,
        address _newFacetAddress,
        IDiamondCut.FacetCutAction _action,
        bytes4[] memory _selectors
    ) internal returns (uint256, bytes32) {
        DiamondStorage storage ds = diamondStorage();
        require(_selectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        if (_action == IDiamondCut.FacetCutAction.Add) {
            require(
                _newFacetAddress != address(0),
                "LibDiamondCut: Add facet can't be address(0)"
            );
            enforceHasContractCode(
                _newFacetAddress, "LibDiamondCut: Add facet has no code"
            );
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                require(
                    address(bytes20(oldFacet)) == address(0),
                    "LibDiamondCut: Can't add function that already exists"
                );
                // add facet for selector
                ds.facets[selector] = bytes20(_newFacetAddress) | bytes32(_selectorCount);
                uint256 selectorInSlotPosition = (_selectorCount % 8) * 32;
                // clear selector position in slot and add selector
                _selectorSlot = (
                    _selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorInSlotPosition)
                ) | (bytes32(selector) >> selectorInSlotPosition);
                // if slot is full then write it to storage
                if (selectorInSlotPosition == 224) {
                    ds.selectorSlots[_selectorCount / 8] = _selectorSlot;
                    _selectorSlot = 0;
                }
                _selectorCount++;
            }
        } else if (_action == IDiamondCut.FacetCutAction.Replace) {
            require(
                _newFacetAddress != address(0),
                "LibDiamondCut: Replace facet can't be address(0)"
            );
            enforceHasContractCode(
                _newFacetAddress, "LibDiamondCut: Replace facet has no code"
            );
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                address oldFacetAddress = address(bytes20(oldFacet));
                // only useful if immutable functions exist
                require(
                    oldFacetAddress != address(this),
                    "LibDiamondCut: Can't replace immutable function"
                );
                require(
                    oldFacetAddress != _newFacetAddress,
                    "LibDiamondCut: Can't replace function with same function"
                );
                require(
                    oldFacetAddress != address(0),
                    "LibDiamondCut: Can't replace function that doesn't exist"
                );
                // replace old facet address
                ds.facets[selector] =
                    (oldFacet & CLEAR_ADDRESS_MASK) | bytes20(_newFacetAddress);
            }
        } else if (_action == IDiamondCut.FacetCutAction.Remove) {
            require(
                _newFacetAddress == address(0),
                "LibDiamondCut: Remove facet address must be address(0)"
            );
            uint256 selectorSlotCount = _selectorCount / 8;
            uint256 selectorInSlotIndex = (_selectorCount % 8) - 1;
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                if (_selectorSlot == 0) {
                    // get last selectorSlot
                    selectorSlotCount--;
                    _selectorSlot = ds.selectorSlots[selectorSlotCount];
                    selectorInSlotIndex = 7;
                }
                bytes4 lastSelector;
                uint256 oldSelectorsSlotCount;
                uint256 oldSelectorInSlotPosition;
                // adding a block here prevents stack too deep error
                {
                    bytes4 selector = _selectors[selectorIndex];
                    bytes32 oldFacet = ds.facets[selector];
                    require(
                        address(bytes20(oldFacet)) != address(0),
                        "LibDiamondCut: Can't remove function that doesn't exist"
                    );
                    // only useful if immutable functions exist
                    require(
                        address(bytes20(oldFacet)) != address(this),
                        "LibDiamondCut: Can't remove immutable function"
                    );
                    // replace selector with last selector in ds.facets
                    // gets the last selector
                    lastSelector = bytes4(_selectorSlot << (selectorInSlotIndex * 32));
                    if (lastSelector != selector) {
                        // update last selector slot position info
                        ds.facets[lastSelector] = (oldFacet & CLEAR_ADDRESS_MASK)
                            | bytes20(ds.facets[lastSelector]);
                    }
                    delete ds.facets[selector];
                    uint256 oldSelectorCount = uint16(uint256(oldFacet));
                    oldSelectorsSlotCount = oldSelectorCount / 8;
                    oldSelectorInSlotPosition = (oldSelectorCount % 8) * 32;
                }
                if (oldSelectorsSlotCount != selectorSlotCount) {
                    bytes32 oldSelectorSlot = ds.selectorSlots[oldSelectorsSlotCount];
                    // clears the selector being deleted and puts the last selector in its place.
                    oldSelectorSlot = (
                        oldSelectorSlot
                            & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition)
                    ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                    // update storage with the modified slot
                    ds.selectorSlots[oldSelectorsSlotCount] = oldSelectorSlot;
                } else {
                    // clears the selector being deleted and puts the last selector in its place.
                    _selectorSlot = (
                        _selectorSlot
                            & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition)
                    ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                }
                if (selectorInSlotIndex == 0) {
                    delete ds.selectorSlots[selectorSlotCount];
                    _selectorSlot = 0;
                }
                selectorInSlotIndex--;
            }
            _selectorCount = selectorSlotCount * 8 + selectorInSlotIndex + 1;
        } else {
            revert("LibDiamondCut: Incorrect FacetCutAction");
        }
        return (_selectorCount, _selectorSlot);
    }

    function initializeDiamondCut(address _init, bytes memory _calldata) internal {
        if (_init == address(0)) {
            require(
                _calldata.length == 0,
                "LibDiamondCut: _init is address(0) but_calldata is not empty"
            );
        } else {
            require(
                _calldata.length > 0,
                "LibDiamondCut: _calldata is empty but _init is not address(0)"
            );
            if (_init != address(this)) {
                enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
            }
            (bool success, bytes memory error) = _init.delegatecall(_calldata);
            if (success == false) {
                if (error.length > 0) {
                    // bubble up the error
                    revert(string(error));
                } else {
                    revert("LibDiamondCut: _init function reverted");
                }
            }
        }
    }

    function enforceHasContractCode(address _contract, string memory _errorMessage)
        internal
        view
    {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

File 13 of 20 : LibShortRecord.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {U256, U88, U80} from "contracts/libraries/PRBMathHelper.sol";

import {STypes, SR} from "contracts/libraries/DataTypes.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {Events} from "contracts/libraries/Events.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {Constants} from "contracts/libraries/Constants.sol";
import {LibAsset} from "contracts/libraries/LibAsset.sol";
import {LibOrders} from "contracts/libraries/LibOrders.sol";
import {LibOracle} from "contracts/libraries/LibOracle.sol";

// import {console} from "contracts/libraries/console.sol";

library LibShortRecord {
    using U256 for uint256;
    using U88 for uint88;
    using U80 for uint80;

    function getCollateralRatio(STypes.ShortRecord memory short, address asset)
        internal
        view
        returns (uint256 cRatio)
    {
        return short.collateral.div(short.ercDebt.mul(LibOracle.getPrice(asset)));
    }

    function getCollateralRatioSpotPrice(
        STypes.ShortRecord memory short,
        uint256 oraclePrice
    ) internal pure returns (uint256 cRatio) {
        return short.collateral.div(short.ercDebt.mul(oraclePrice));
    }

    /**
     * @notice Returns number of active shortRecords
     *
     * @param asset The market that will be impacted
     * @param shorter Shorter address
     *
     * @return shortRecordCount
     */
    function getShortRecordCount(address asset, address shorter)
        internal
        view
        returns (uint256 shortRecordCount)
    {
        AppStorage storage s = appStorage();

        // Retrieve first non-HEAD short
        uint8 id = s.shortRecords[asset][shorter][Constants.HEAD].nextId;
        if (id <= Constants.HEAD) {
            return 0;
        }

        while (true) {
            shortRecordCount++;
            // One short of one shorter in this order book
            STypes.ShortRecord storage currentShort = s.shortRecords[asset][shorter][id];
            // Move to next short unless this is the last one
            if (currentShort.nextId > Constants.HEAD) {
                id = currentShort.nextId;
            } else {
                return shortRecordCount;
            }
        }
    }

    function createShortRecord(
        address asset,
        address shorter,
        SR status,
        uint88 collateral,
        uint88 ercAmount,
        uint64 ercDebtRate,
        uint80 dethYieldRate,
        uint40 tokenId
    ) internal returns (uint8 id) {
        AppStorage storage s = appStorage();

        // ensure the tokenId can be downcasted to 40 bits
        if (tokenId > type(uint40).max) revert Errors.InvalidTokenId();

        uint8 nextId;
        (id, nextId) = setShortRecordIds(asset, shorter);

        if (id <= Constants.SHORT_MAX_ID) {
            s.shortRecords[asset][shorter][id] = STypes.ShortRecord({
                prevId: Constants.HEAD,
                id: id,
                nextId: nextId,
                status: status,
                collateral: collateral,
                ercDebt: ercAmount,
                ercDebtRate: ercDebtRate,
                dethYieldRate: dethYieldRate,
                flaggerId: 0,
                flaggedAt: 0,
                tokenId: tokenId,
                updatedAt: LibOrders.getOffsetTime()
            });
            emit Events.CreateShortRecord(asset, shorter, id);
        } else {
            // All shortRecordIds used, combine into max shortRecordId
            id = Constants.SHORT_MAX_ID;
            fillShortRecord(
                asset,
                shorter,
                id,
                status,
                collateral,
                ercAmount,
                ercDebtRate,
                dethYieldRate
            );
        }
    }

    function transferShortRecord(
        address asset,
        address from,
        address to,
        uint40 tokenId,
        STypes.NFT memory nft
    ) internal {
        AppStorage storage s = appStorage();
        STypes.ShortRecord storage short = s.shortRecords[asset][from][nft.shortRecordId];
        if (short.status == SR.Closed) revert Errors.OriginalShortRecordCancelled();
        if (short.flaggerId != 0) revert Errors.CannotTransferFlaggedShort();

        // @dev should match CR in flagShort() to prevent front-running that denies flag
        uint256 cRatio =
            getCollateralRatioSpotPrice(short, LibOracle.getSavedOrSpotOraclePrice(asset));
        if (cRatio < LibAsset.primaryLiquidationCR(asset)) {
            revert Errors.CannotTransferFlaggableShort();
        }

        short.tokenId = 0;
        deleteShortRecord(asset, from, nft.shortRecordId);

        uint8 id = createShortRecord(
            asset,
            to,
            SR.FullyFilled,
            short.collateral,
            short.ercDebt,
            short.ercDebtRate,
            short.dethYieldRate,
            tokenId
        );

        if (id == Constants.SHORT_MAX_ID) {
            revert Errors.ReceiverExceededShortRecordLimit();
        }

        s.nftMapping[tokenId].owner = to;
        s.nftMapping[tokenId].shortRecordId = id;
    }

    function fillShortRecord(
        address asset,
        address shorter,
        uint8 shortId,
        SR status,
        uint88 collateral,
        uint88 ercAmount,
        uint64 ercDebtRate,
        uint80 dethYieldRate
    ) internal {
        AppStorage storage s = appStorage();

        STypes.ShortRecord storage short = s.shortRecords[asset][shorter][shortId];
        if (short.status == SR.Closed) {
            // No need to blend/merge components if the shortRecord was closed, simply overwrite
            short.ercDebt = ercAmount;
            short.ercDebtRate = ercDebtRate;
            short.collateral = collateral;
            short.dethYieldRate = dethYieldRate;
            short.updatedAt = LibOrders.getOffsetTime();
            short.flaggerId = 0;
            short.flaggedAt = 0;
        } else {
            uint256 ercDebtSocialized = ercAmount.mul(ercDebtRate);
            uint256 yield = collateral.mul(dethYieldRate);
            merge(
                short,
                ercAmount,
                ercDebtSocialized,
                collateral,
                yield,
                LibOrders.getOffsetTime()
            );
        }
        // @dev Must be set after if statement eval
        short.status = status;
    }

    function deleteShortRecord(address asset, address shorter, uint8 id) internal {
        AppStorage storage s = appStorage();

        STypes.ShortRecord storage shortRecord = s.shortRecords[asset][shorter][id];
        // Because of the onlyValidShortRecord modifier, only cancelShort can pass SR.Closed
        // Don't recycle shortRecord id 254 so it can be used for all overflow uint8 ids
        if (shortRecord.status != SR.PartialFill && id < Constants.SHORT_MAX_ID) {
            // remove the links of ID in the market
            // @dev (ID) is exiting, [ID] is inserted
            // BEFORE: PREV <-> (ID) <-> NEXT
            // AFTER : PREV <----------> NEXT
            s.shortRecords[asset][shorter][shortRecord.prevId].nextId = shortRecord.nextId;
            if (shortRecord.nextId != Constants.HEAD) {
                s.shortRecords[asset][shorter][shortRecord.nextId].prevId =
                    shortRecord.prevId;
            }
            // Make reuseable for future short records
            uint8 prevHEAD = s.shortRecords[asset][shorter][Constants.HEAD].prevId;
            s.shortRecords[asset][shorter][Constants.HEAD].prevId = id;
            // Move the cancelled ID behind HEAD to re-use it
            // note: C_IDs (cancelled ids) only need to point back (set prevId, can retain nextId)
            // BEFORE: .. C_ID2 <- C_ID1 <--------- HEAD <-> ... [ID]
            // AFTER1: .. C_ID2 <- C_ID1 <- [ID] <- HEAD <-> ...
            if (prevHEAD > Constants.HEAD) {
                shortRecord.prevId = prevHEAD;
            } else {
                // if this is the first ID cancelled
                // HEAD.prevId needs to be HEAD
                // and one of the cancelled id.prevID should point to HEAD
                // BEFORE: HEAD <--------- HEAD <-> ... [ID]
                // AFTER1: HEAD <- [ID] <- HEAD <-> ...
                shortRecord.prevId = Constants.HEAD;
            }

            //Event for delete SR is emitted here and not at the top level because
            //SR may be cancelled, but there might tied to an active short order
            //The code above is hit when that SR id is ready for reuse
            emit Events.DeleteShortRecord(asset, shorter, id);
        }

        shortRecord.status = SR.Closed;
    }

    function createTappSR(address asset) internal returns (uint8 id) {
        AppStorage storage s = appStorage();
        address shorter = address(this);

        STypes.ShortRecord storage headSR = s.shortRecords[asset][shorter][Constants.HEAD];
        headSR.prevId = Constants.HEAD;
        headSR.nextId = Constants.SHORT_STARTING_ID;

        STypes.AssetUser storage AssetUser = s.assetUser[asset][shorter];
        AssetUser.shortRecordIdCounter = Constants.SHORT_STARTING_ID + 1;

        s.shortRecords[asset][shorter][Constants.SHORT_STARTING_ID] = STypes.ShortRecord({
            prevId: Constants.HEAD,
            id: Constants.SHORT_STARTING_ID,
            nextId: Constants.HEAD,
            status: SR.FullyFilled,
            collateral: 0,
            ercDebt: 0,
            ercDebtRate: 0,
            dethYieldRate: 0,
            flaggerId: 0,
            flaggedAt: 0,
            tokenId: 0,
            updatedAt: LibOrders.getOffsetTime()
        });
        emit Events.CreateShortRecord(asset, shorter, id);
    }

    function setShortRecordIds(address asset, address shorter)
        private
        returns (uint8 id, uint8 nextId)
    {
        AppStorage storage s = appStorage();

        STypes.ShortRecord storage headSR = s.shortRecords[asset][shorter][Constants.HEAD];
        STypes.AssetUser storage AssetUser = s.assetUser[asset][shorter];
        // Initialize HEAD in case of first short createShortRecord
        if (AssetUser.shortRecordIdCounter == 0) {
            AssetUser.shortRecordIdCounter = Constants.SHORT_STARTING_ID;
            headSR.prevId = Constants.HEAD;
            headSR.nextId = Constants.HEAD;
        }
        // BEFORE: HEAD <-> .. <-> PREV <--------------> NEXT
        // AFTER1: HEAD <-> .. <-> PREV <-> (NEW ID) <-> NEXT
        // place created short next to HEAD
        nextId = headSR.nextId;
        uint8 canceledId = headSR.prevId;
        // @dev (ID) is exiting, [ID] is inserted
        // in this case, the protocol re-uses (ID) and moves it to [ID]
        // check if a previously closed short exists
        if (canceledId > Constants.HEAD) {
            // BEFORE: CancelledID <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
            // AFTER1: CancelledID <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
            uint8 prevCanceledId = s.shortRecords[asset][shorter][canceledId].prevId;
            if (prevCanceledId > Constants.HEAD) {
                headSR.prevId = prevCanceledId;
            } else {
                // BEFORE: HEAD <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
                // AFTER1: HEAD <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
                headSR.prevId = Constants.HEAD;
            }
            // re-use the previous order's id
            id = canceledId;
        } else {
            // BEFORE: HEAD <-> .. <-> PREV <--------------> NEXT
            // AFTER1: HEAD <-> .. <-> PREV <-> (NEW ID) <-> NEXT
            // otherwise just increment to a new short record id
            // and the short record grows in height/size
            id = AssetUser.shortRecordIdCounter;
            // Avoids overflow revert, prevents DOS on uint8
            if (id < type(uint8).max) {
                AssetUser.shortRecordIdCounter += 1;
            } else {
                // If max id reached, match into max shortRecordId
                return (id, nextId);
            }
        }

        if (nextId > Constants.HEAD) {
            s.shortRecords[asset][shorter][nextId].prevId = id;
        }
        headSR.nextId = id;
    }

    function updateErcDebt(address asset, address shorter, uint8 shortId) internal {
        AppStorage storage s = appStorage();

        STypes.ShortRecord storage short = s.shortRecords[asset][shorter][shortId];

        // Distribute ercDebt
        uint64 ercDebtRate = s.asset[asset].ercDebtRate;
        uint88 ercDebt = short.ercDebt.mulU88(ercDebtRate - short.ercDebtRate);

        if (ercDebt > 0) {
            short.ercDebt += ercDebt;
            short.ercDebtRate = ercDebtRate;
        }
    }

    function updateErcDebt(STypes.ShortRecord storage short, address asset) internal {
        AppStorage storage s = appStorage();

        // Distribute ercDebt
        uint64 ercDebtRate = s.asset[asset].ercDebtRate;
        uint88 ercDebt = short.ercDebt.mulU88(ercDebtRate - short.ercDebtRate);

        if (ercDebt > 0) {
            short.ercDebt += ercDebt;
            short.ercDebtRate = ercDebtRate;
        }
    }

    function merge(
        STypes.ShortRecord storage short,
        uint88 ercDebt,
        uint256 ercDebtSocialized,
        uint88 collateral,
        uint256 yield,
        uint32 creationTime
    ) internal {
        // Resolve ercDebt
        ercDebtSocialized += short.ercDebt.mul(short.ercDebtRate);
        short.ercDebt += ercDebt;
        short.ercDebtRate = ercDebtSocialized.divU64(short.ercDebt);
        // Resolve dethCollateral
        yield += short.collateral.mul(short.dethYieldRate);
        short.collateral += collateral;
        short.dethYieldRate = yield.divU80(short.collateral);
        // Assign updatedAt
        short.updatedAt = creationTime;
    }

    function disburseCollateral(
        address asset,
        address shorter,
        uint88 collateral,
        uint256 dethYieldRate,
        uint32 updatedAt
    ) internal {
        AppStorage storage s = appStorage();

        STypes.Asset storage Asset = s.asset[asset];
        uint256 vault = Asset.vault;
        STypes.Vault storage Vault = s.vault[vault];

        Vault.dethCollateral -= collateral;
        Asset.dethCollateral -= collateral;
        // Distribute yield
        uint88 yield = collateral.mulU88(Vault.dethYieldRate - dethYieldRate);
        if (yield > 0) {
            /*
            @dev If somebody exits a short, gets liquidated, decreases their collateral before YIELD_DELAY_SECONDS duration is up,
            they lose their yield to the TAPP
            */
            bool isNotRecentlyModified =
                LibOrders.getOffsetTime() - updatedAt > Constants.YIELD_DELAY_SECONDS;
            if (isNotRecentlyModified) {
                s.vaultUser[vault][shorter].ethEscrowed += yield;
            } else {
                s.vaultUser[vault][address(this)].ethEscrowed += yield;
            }
        }
    }

    function burnNFT(uint256 tokenId) internal {
        //@dev No need to check downcast tokenId because it is handled in function that calls burnNFT
        AppStorage storage s = appStorage();
        STypes.NFT storage nft = s.nftMapping[tokenId];
        if (nft.owner == address(0)) revert Errors.NotMinted();
        address asset = s.assetMapping[nft.assetId];
        STypes.ShortRecord storage short =
            s.shortRecords[asset][nft.owner][nft.shortRecordId];
        delete s.nftMapping[tokenId];
        delete s.getApproved[tokenId];
        delete short.tokenId;
        emit Events.Transfer(nft.owner, address(0), tokenId);
    }

    function setFlagger(
        STypes.ShortRecord storage short,
        address dusd,
        uint16 flaggerHint
    ) internal {
        //@dev the flagMapping is global (represented by dusd), not asset specific
        AppStorage storage s = appStorage();
        STypes.AssetUser storage flagStorage = s.assetUser[dusd][msg.sender];

        //@dev Whenever a new flagger flags, use the flaggerIdCounter.
        if (flagStorage.g_flaggerId == 0) {
            address flaggerToReplace = s.flagMapping[flaggerHint];
            uint256 timeDiff = flaggerToReplace != address(0)
                ? LibOrders.getOffsetTime() - s.assetUser[dusd][flaggerToReplace].g_flaggedAt
                : 0;
            // @dev re-use an inactive flaggerId
            // @dev If secondLiquidationTime is the same across assets, will not be a problem to use dusd
            if (timeDiff > LibAsset.secondLiquidationTime(dusd)) {
                delete s.assetUser[dusd][flaggerToReplace].g_flaggerId;
                short.flaggerId = flagStorage.g_flaggerId = flaggerHint;
            } else if (s.flaggerIdCounter < type(uint24).max) {
                //@dev generate brand new flaggerId
                short.flaggerId = flagStorage.g_flaggerId = s.flaggerIdCounter;
                s.flaggerIdCounter++;
            } else {
                revert Errors.InvalidFlaggerHint();
            }
            s.flagMapping[short.flaggerId] = msg.sender;
        } else {
            //@dev re-use flaggerId if flagger has an existing one
            short.flaggerId = flagStorage.g_flaggerId;
        }
        short.flaggedAt = flagStorage.g_flaggedAt = LibOrders.getOffsetTime();
    }

    //@dev reset flag info if new cratio is above primaryLiquidationCR
    function maybeResetFlag(STypes.ShortRecord storage short, address asset) internal {
        if (short.flaggerId != 0) {
            if (getCollateralRatio(short, asset) >= LibAsset.primaryLiquidationCR(asset))
            {
                resetFlag(short);
            }
        }
    }

    function resetFlag(STypes.ShortRecord storage shortRecord) internal {
        delete shortRecord.flaggerId;
        delete shortRecord.flaggedAt;
    }
}

File 14 of 20 : IAsset.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;



interface IAsset {

  // functions from node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol
  function name() external view returns (string memory);
  function symbol() external view returns (string memory);
  function decimals() external view returns (uint8);
  function totalSupply() external view returns (uint256);
  function balanceOf(address account) external view returns (uint256);
  function transfer(address to, uint256 amount) external returns (bool);
  function allowance(address owner, address spender) external view returns (uint256);
  function approve(address spender, uint256 amount) external returns (bool);
  function transferFrom(address from, address to, uint256 amount) external returns (bool);
  function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
  function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);

  // functions from node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol
  function eip712Domain() external view returns (bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions);

  // functions from node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol
  function permit(
        address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
  function nonces(address owner) external view returns (uint256);
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  // functions from contracts/tokens/Asset.sol
  function mint(address to, uint256 amount) external;
  function burnFrom(address account, uint256 amount) external;
}

File 15 of 20 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 16 of 20 : 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 17 of 20 : IDiamond.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {IDiamondLoupe} from "contracts/interfaces/IDiamondLoupe.sol";
import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol";
import "contracts/libraries/DataTypes.sol";
import "test/utils/TestTypes.sol";

interface IDiamond {

  // functions from contracts/Diamond.sol
  fallback() external payable;
  receive() external payable;
  // functions from contracts/facets/DiamondCutFacet.sol
  function diamondCut(
        IDiamondCut.FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
  // functions from contracts/facets/OwnerFacet.sol
  function createMarket(address asset, STypes.Asset memory a) external;
  function owner() external view returns (address);
  function admin() external view returns (address);
  function ownerCandidate() external view returns (address);
  function transferOwnership(address newOwner) external;
  function claimOwnership() external;
  function transferAdminship(address newAdmin) external;
  function setAssetOracle(address asset, address oracle) external;
  function createVault(
        address deth, uint256 vault, MTypes.CreateVaultParams calldata params) external;
  function setTithe(uint256 vault, uint16 dethTithePercent) external;
  function setDittoMatchedRate(uint256 vault, uint16 rewardRate) external;
  function setDittoShorterRate(uint256 vault, uint16 rewardRate) external;
  function setInitialCR(address asset, uint16 value) external;
  function setPrimaryLiquidationCR(address asset, uint16 value) external;
  function setSecondaryLiquidationCR(address asset, uint16 value) external;
  function setForcedBidPriceBuffer(address asset, uint8 value) external;
  function setMinimumCR(address asset, uint8 value) external;
  function setResetLiquidationTime(address asset, uint8 value) external;
  function setSecondLiquidationTime(address asset, uint8 value) external;
  function setFirstLiquidationTime(address asset, uint8 value) external;
  function setTappFeePct(address asset, uint8 value) external;
  function setCallerFeePct(address asset, uint8 value) external;
  function setMinBidEth(address asset, uint8 value) external;
  function setMinAskEth(address asset, uint8 value) external;
  function setMinShortErc(address asset, uint16 value) external;
  function createBridge(
        address bridge, uint256 vault, uint16 withdrawalFee, uint8 unstakeFee) external;
  function setWithdrawalFee(address bridge, uint16 withdrawalFee) external;
  function setUnstakeFee(address bridge, uint8 unstakeFee) external;
  // functions from contracts/facets/PrimaryLiquidationFacet.sol
  function flagShort(address asset, address shorter, uint8 id, uint16 flaggerHint) external;
  function liquidate(
        address asset, address shorter, uint8 id, uint16[] memory shortHintArray) external returns (uint88, uint88);
  // functions from contracts/facets/AskOrdersFacet.sol
  function createAsk(
        address asset, uint80 price, uint88 ercAmount, bool isMarketOrder, MTypes.OrderHint[] calldata orderHintArray) external;
  function _cancelAsk(address asset, uint16 id) external;
  function _cancelShort(address asset, uint16 id) external;
  // functions from contracts/facets/TWAPFacet.sol
  function estimateWETHInUSDC(uint128 amountIn, uint32 secondsAgo) external view returns (uint256 amountOut);
  // functions from contracts/facets/ViewFacet.sol
  function getDethBalance(uint256 vault, address user) external view returns (uint256);
  function getAssetBalance(address asset, address user) external view returns (uint256);
  function getVault(address asset) external view returns (uint256);
  function getBridgeVault(address bridge) external view returns (uint256);
  function getDethYieldRate(uint256 vault) external view returns (uint256);
  function getBids(address asset) external view returns (STypes.Order[] memory);
  function getAsks(address asset) external view returns (STypes.Order[] memory);
  function getShorts(address asset) external view returns (STypes.Order[] memory);
  function getBidHintId(address asset, uint256 price) external view returns (uint16 hintId);
  function getAskHintId(address asset, uint256 price) external view returns (uint16 hintId);
  function getShortHintId(address asset, uint256 price) external view returns (uint16);
  function getShortIdAtOracle(address asset) external view returns (uint16 shortHintId);
  function getHintArray(address asset, uint256 price, O orderType, uint256 numHints) external view returns (MTypes.OrderHint[] memory orderHintArray);
  function getCollateralRatio(address asset, STypes.ShortRecord memory short) external view returns (uint256 cRatio);
  function getCollateralRatioSpotPrice(address asset, STypes.ShortRecord memory short) external view returns (uint256 cRatio);
  function getOracleAssetPrice(address asset) external view returns (uint256);
  function getProtocolAssetPrice(address asset) external view returns (uint256);
  function getProtocolAssetTime(address asset) external view returns (uint256);
  function getTithe(uint256 vault) external view returns (uint256);
  function getUndistributedYield(uint256 vault) external view returns (uint256);
  function getYield(address asset, address user) external view returns (uint256 shorterYield);
  function getDittoMatchedReward(uint256 vault, address user) external view returns (uint256);
  function getDittoReward(uint256 vault, address user) external view returns (uint256);
  function getAssetCollateralRatio(address asset) external view returns (uint256 cRatio);
  function getShortRecords(address asset, address shorter) external view returns (STypes.ShortRecord[] memory shorts);
  function getShortRecord(address asset, address shorter, uint8 id) external view returns (STypes.ShortRecord memory shortRecord);
  function getShortRecordCount(address asset, address shorter) external view returns (uint256 shortRecordCount);
  function getAssetUserStruct(address asset, address user) external view returns (STypes.AssetUser memory);
  function getVaultUserStruct(uint256 vault, address user) external view returns (STypes.VaultUser memory);
  function getVaultStruct(uint256 vault) external view returns (STypes.Vault memory);
  function getAssetStruct(address asset) external view returns (STypes.Asset memory);
  function getBridgeStruct(address bridge) external view returns (STypes.Bridge memory);
  function getOffsetTime() external view returns (uint256);
  function getFlaggerId(address asset, address user) external view returns (uint24 flaggerId);
  function getFlaggerHint() external view returns (uint24 flaggerId);
  // functions from contracts/facets/DiamondLoupeFacet.sol
  function facets() external view returns (IDiamondLoupe.Facet[] memory facets_);
  function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory _facetFunctionSelectors);
  function facetAddresses() external view returns (address[] memory facetAddresses_);
  function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
  // functions from contracts/facets/TestFacet.sol
  function setFrozenT(address asset, F value) external;
  function setprimaryLiquidationCRT(address asset, uint16 value) external;
  function getAskKey(address asset, uint16 id) external view returns (uint16 prevId, uint16 nextId);
  function getBidKey(address asset, uint16 id) external view returns (uint16 prevId, uint16 nextId);
  function getBidOrder(address asset, uint16 id) external view returns (STypes.Order memory bid);
  function getAskOrder(address asset, uint16 id) external view returns (STypes.Order memory ask);
  function getShortOrder(address asset, uint16 id) external view returns (STypes.Order memory short);
  function currentInactiveBids(address asset) external view returns (STypes.Order[] memory);
  function currentInactiveAsks(address asset) external view returns (STypes.Order[] memory);
  function currentInactiveShorts(address asset) external view returns (STypes.Order[] memory);
  function setReentrantStatus(uint8 reentrantStatus) external;
  function getReentrantStatus() external view returns (uint256);
  function getAssetNormalizedStruct(address asset) external view returns (TestTypes.AssetNormalizedStruct memory);
  function getBridgeNormalizedStruct(address bridge) external view returns (TestTypes.BridgeNormalizedStruct memory);
  function setOracleTimeAndPrice(address asset, uint256 price) external;
  function getOracleTimeT(address asset) external view returns (uint256 oracleTime);
  function getOraclePriceT(address asset) external view returns (uint80 oraclePrice);
  function setStartingShortId(address asset, uint16 id) external;
  function updateStartingShortId(address asset, uint16[] calldata shortHintArray) external;
  function nonZeroVaultSlot0(uint256 vault) external;
  function setforcedBidPriceBufferT(address asset, uint8 value) external;
  function setErcDebtRate(address asset, uint64 value) external;
  function setOrderIdT(address asset, uint16 value) external;
  function setEthEscrowed(address addr, uint88 eth) external;
  function setErcEscrowed(address asset, address addr, uint104 erc) external;
  function getUserOrders(address asset, address addr, O orderType) external view returns (STypes.Order[] memory orders);
  function getAssets() external view returns (address[] memory);
  function getAssetsMapping(uint256 assetId) external view returns (address);
  function setTokenId(uint40 tokenId) external;
  function getTokenId() external view returns (uint40 tokenId);
  function getNFT(uint256 tokenId) external view returns (STypes.NFT memory nft);
  function getNFTName() external view returns (string memory);
  function getNFTSymbol() external view returns (string memory);
  function setFlaggerIdCounter(uint24 flaggerIdCounter) external;
  function getFlaggerIdCounter() external view returns (uint24 flaggerId);
  function getFlagger(uint24 flaggerId) external view returns (address flagger);
  function dittoShorterRate(uint256 vault) external view returns (uint256);
  function dittoMatchedRate(uint256 vault) external view returns (uint256);
  function deleteBridge(address bridge) external;
  // functions from contracts/facets/BridgeRouterFacet.sol
  function getDethTotal(uint256 vault) external view returns (uint256);
  function getBridges(uint256 vault) external view returns (address[] memory);
  function deposit(address bridge, uint88 amount) external;
  function depositEth(address bridge) external payable;
  function withdraw(address bridge, uint88 dethAmount) external;
  function unstakeEth(address bridge, uint88 dethAmount) external;
  function withdrawTapp(address bridge, uint88 dethAmount) external;
  // functions from contracts/facets/ExitShortFacet.sol
  function exitShortWallet(address asset, uint8 id, uint88 buyBackAmount) external;
  function exitShortErcEscrowed(address asset, uint8 id, uint88 buyBackAmount) external;
  function exitShort(
        address asset, uint8 id, uint88 buyBackAmount, uint80 price, uint16[] memory shortHintArray) external;
  // functions from contracts/facets/ShortRecordFacet.sol
  function increaseCollateral(address asset, uint8 id, uint88 amount) external;
  function decreaseCollateral(address asset, uint8 id, uint88 amount) external;
  function combineShorts(address asset, uint8[] memory ids) external;
  // functions from contracts/facets/OrdersFacet.sol
  function cancelBid(address asset, uint16 id) external;
  function cancelAsk(address asset, uint16 id) external;
  function cancelShort(address asset, uint16 id) external;
  function cancelOrderFarFromOracle(
        address asset, O orderType, uint16 lastOrderId, uint16 numOrdersToCancel) external;
  // functions from contracts/facets/ShortOrdersFacet.sol
  function createLimitShort(
        address asset, uint80 price, uint88 ercAmount, MTypes.OrderHint[] memory orderHintArray, uint16[] memory shortHintArray, uint16 initialCR) external;
  // functions from contracts/facets/ERC721Facet.sol
  function balanceOf(address owner) external view returns (uint256 balance);
  function ownerOf(uint256 tokenId) external view returns (address);
  function safeTransferFrom(address from, address to, uint256 tokenId) external;
  function safeTransferFrom(
        address from, address to, uint256 tokenId, bytes memory data) external;
  function transferFrom(address from, address to, uint256 tokenId) external;
  function isApprovedForAll(address owner, address operator) external view returns (bool);
  function approve(address to, uint256 tokenId) external;
  function setApprovalForAll(address operator, bool approved) external;
  function getApproved(uint256 tokenId) external view returns (address operator);
  function mintNFT(address asset, uint8 shortRecordId) external;
  function tokenURI(uint256 id) external view returns (string memory);
  function supportsInterface(bytes4 _interfaceId) external view returns (bool);
  // functions from contracts/facets/YieldFacet.sol
  function updateYield(uint256 vault) external;
  function distributeYield(address[] calldata assets) external;
  function claimDittoMatchedReward(uint256 vault) external;
  function withdrawDittoReward(uint256 vault) external;
  // functions from contracts/facets/VaultFacet.sol
  function depositDETH(address deth, uint88 amount) external;
  function depositAsset(address asset, uint104 amount) external;
  function withdrawDETH(address deth, uint88 amount) external;
  function withdrawAsset(address asset, uint104 amount) external;
  // functions from contracts/facets/BidOrdersFacet.sol
  function createBid(
        address asset, uint80 price, uint88 ercAmount, bool isMarketOrder, MTypes.OrderHint[] calldata orderHintArray, uint16[] calldata shortHintArray) external returns (uint88 ethFilled, uint88 ercAmountLeft);
  function createForcedBid(
        address sender, address asset, uint80 price, uint88 ercAmount, uint16[] calldata shortHintArray) external returns (uint88 ethFilled, uint88 ercAmountLeft);
  // functions from contracts/facets/SecondaryLiquidationFacet.sol
  function liquidateSecondary(
        address asset, MTypes.BatchLiquidation[] memory batches, uint88 liquidateAmount, bool isWallet) external;
  // functions from contracts/facets/MarketShutdownFacet.sol
  function shutdownMarket(address asset) external;
  function redeemErc(address asset, uint88 amtWallet, uint88 amtEscrow) external;
}

File 18 of 20 : IDiamondCut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
/*
 * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
 */

interface IDiamondCut {
    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

File 19 of 20 : IDiamondLoupe.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/*
 * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
 * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
 */

// A loupe is a small magnifying glass used to look at diamonds.
// These functions look at diamonds
interface IDiamondLoupe {
    /// These functions are expected to be called frequently
    /// by tools.

    struct Facet {
        address facetAddress;
        bytes4[] functionSelectors;
    }

    /// @notice Gets all facet addresses and their four byte function selectors.
    /// @return facets_ Facet
    function facets() external view returns (Facet[] memory facets_);

    /// @notice Gets all the function selectors supported by a specific facet.
    /// @param _facet The facet address.
    /// @return facetFunctionSelectors_
    function facetFunctionSelectors(address _facet)
        external
        view
        returns (bytes4[] memory facetFunctionSelectors_);

    /// @notice Get all the facet addresses used by a diamond.
    /// @return facetAddresses_
    function facetAddresses() external view returns (address[] memory facetAddresses_);

    /// @notice Gets the facet that supports the given selector.
    /// @dev If facet is not found return address(0).
    /// @param _functionSelector The function selector.
    /// @return facetAddress_ The facet address.
    function facetAddress(bytes4 _functionSelector)
        external
        view
        returns (address facetAddress_);
}

File 20 of 20 : TestTypes.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {F} from "contracts/libraries/DataTypes.sol";

enum PrimaryScenarios {
    CRatioBetween110And200,
    CRatioBelow110,
    CRatioBelow110BlackSwan
}
// @dev only used for testing

enum SecondaryScenarios {
    CRatioBetween110And150,
    CRatioBetween100And110,
    CRatioBelow100
}
// @dev only used for testing

enum SecondaryType {
    LiquidateErcEscrowed,
    LiquidateWallet
}

library TestTypes {
    struct StorageUser {
        address addr;
        uint256 ethEscrowed;
        uint256 ercEscrowed;
    }

    struct AssetNormalizedStruct {
        F frozen;
        uint16 orderId;
        uint256 initialCR;
        uint256 primaryLiquidationCR;
        uint256 secondaryLiquidationCR;
        uint256 forcedBidPriceBuffer;
        uint256 minimumCR;
        uint256 tappFeePct;
        uint256 callerFeePct;
        uint256 resetLiquidationTime;
        uint256 secondLiquidationTime;
        uint256 firstLiquidationTime;
        uint16 startingShortId;
        uint256 minBidEth;
        uint256 minAskEth;
        uint256 minShortErc;
        uint8 assetId;
    }

    struct BridgeNormalizedStruct {
        uint256 withdrawalFee;
        uint256 unstakeFee;
    }

    struct MockOracleData {
        uint80 roundId;
        int256 answer;
        uint256 startedAt;
        uint256 updatedAt;
        uint80 answeredInRound;
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "interfaces/=interfaces/",
    "contracts/=contracts/",
    "test/=test/",
    "test-gas/=test-gas/",
    "deploy/=deploy/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@chainlink/=node_modules/@chainlink/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "@prb/=node_modules/@prb/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AssetIsFrozen","type":"error"},{"inputs":[],"name":"BadHintIdArray","type":"error"},{"inputs":[],"name":"BadShortHint","type":"error"},{"inputs":[],"name":"InsufficientETHEscrowed","type":"error"},{"inputs":[],"name":"InsufficientEthInLiquidityPool","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidInitialCR","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"InvalidTokenId","type":"error"},{"inputs":[],"name":"InvalidTwapPrice","type":"error"},{"inputs":[],"name":"NotActiveOrder","type":"error"},{"inputs":[],"name":"OrderUnderMinimumSize","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PRBMath_MulDiv18_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint16","name":"id","type":"uint16"},{"indexed":true,"internalType":"enum O","name":"orderType","type":"uint8"}],"name":"CancelOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"enum O","name":"orderType","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"id","type":"uint16"},{"indexed":false,"internalType":"uint88","name":"ercAmount","type":"uint88"}],"name":"CreateOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint16","name":"id","type":"uint16"}],"name":"CreateShortRecord","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"enum O","name":"orderType","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"id","type":"uint16"},{"indexed":false,"internalType":"uint88","name":"fillEth","type":"uint88"},{"indexed":false,"internalType":"uint88","name":"fillErc","type":"uint88"}],"name":"MatchOrder","type":"event"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint80","name":"price","type":"uint80"},{"internalType":"uint88","name":"ercAmount","type":"uint88"},{"components":[{"internalType":"uint16","name":"hintId","type":"uint16"},{"internalType":"uint256","name":"creationTime","type":"uint256"}],"internalType":"struct MTypes.OrderHint[]","name":"orderHintArray","type":"tuple[]"},{"internalType":"uint16[]","name":"shortHintArray","type":"uint16[]"},{"internalType":"uint16","name":"initialCR","type":"uint16"}],"name":"createLimitShort","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561000f575f80fd5b506158ab806200001e5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063f23811cc1461002d575b5f80fd5b61004061003b366004615452565b610042565b005b855f73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547e01000000000000000000000000000000000000000000000000000000000000900460ff16600181111561009f5761009f61551e565b146100d6576040517f0e27a3f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87165f9081526008602052604081205488917f010000000000000000000000000000000000000000000000000000000000000090910460ff16900361015c576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c010000000000000000000000000000000000000000000000000000000090910460ff16016101db576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c02000000000000000000000000000000000000000000000000000000001790556040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915273ffffffffffffffffffffffffffffffffffffffff89165f90815260086020526040812090610281866106b2565b825490915061ffff8088167c01000000000000000000000000000000000000000000000000000000009092041611806102c2575067d02ab486cedc00008110155b156102f9576040517f56cd57b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61031c69ffffffffffffffffffff8b166affffffffffffffffffffff8b166106db565b602084015261032a8b6106f9565b60608401526103388b61074a565b604084018190526affffffffffffffffffffff8a161080610360575082606001518360200151105b15610397576040517faacd1fa000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208301516103a69082610790565b82547f0100000000000000000000000000000000000000000000000000000000000000900460ff165f9081526007602090815260408083203384529091529020546affffffffffffffffffffff16101561042c576040517f7d243a2c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610180810182525f9181018290526080810182905260c081018290526101008101829052610120810182905261014081018290526101608101919091523360e082015269ffffffffffffffffffff8b1660208201526affffffffffffffffffffff8a16815282547a010000000000000000000000000000000000000000000000000000900461ffff166060820152600560a08201526104cd61079b565b63ffffffff1660c082015261ffff80881661012083015273ffffffffffffffffffffffffffffffffffffffff8d165f818152600a6020818152604080842060018552808352818520547901000000000000000000000000000000000000000000000000009004871660808c019081529585529282529351909416825283522090820151815469ffffffffffffffffffff9182166b010000000000000000000000909104909116108015906105b55750600181547b01000000000000000000000000000000000000000000000000000000900460ff1660078111156105b3576105b361551e565b145b15610615576106158d6106028f73ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020546affffffffffffffffffffff1690565b69ffffffffffffffffffff16848c6107af565b61061e8d610840565b60a08601819052602083015169ffffffffffffffffffff16101561064c576106478d838c61090d565b61065c565b61065c8d838c8860600151610b01565b5050600280547fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c01000000000000000000000000000000000000000000000000000000001790555050505050505050505050565b5f60646106cb61ffff8416670de0b6b3a7640000615578565b6106d591906155bc565b92915050565b5f6106f28369ffffffffffffffffffff16836111ea565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260086020526040812060010154819060649061074090610100900460ff16670de0b6b3a7640000615578565b6106f291906155bc565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526008602052604081206001015481906106f29062010000900461ffff16670de0b6b3a7640000615578565b5f6106f283836111ea565b5f6107aa6362f6fc65426155cf565b905090565b5f83836020015169ffffffffffffffffffff16106107fd576611c37937e080006107f58586866020015169ffffffffffffffffffff166107ef91906155cf565b906112f3565b119050610829565b6611c37937e0800061082585856020015169ffffffffffffffffffff16876107ef91906155cf565b1190505b8015610839576108398583611307565b5050505050565b5f6103846108a18373ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6108a961079b565b63ffffffff166108b991906155cf565b10156108ff575073ffffffffffffffffffffffffffffffffffffffff165f908152600a602090815260408083206001845290915290205469ffffffffffffffffffff1690565b6106d5826118dd565b919050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600c6020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529083205491850151839269ffffffffffffffffffff6b01000000000000000000000090910481169116108061099b575061ffff81166001145b156109a957600191506109ba565b6109b783600c018786611b4b565b91505b6109c983600c01878785611cef565b6109d38686612553565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526008840160205260408120546101208701517f010000000000000000000000000000000000000000000000000000000000000090910460ff169190610a7590610a36906106b2565b60208901518951610a62916affffffffffffffffffffff9091169069ffffffffffffffffffff1661273f565b6affffffffffffffffffffff169061273f565b5f838152600787016020908152604080832060e08c015173ffffffffffffffffffffffffffffffffffffffff168452909152812080549293508392909190610acb9084906affffffffffffffffffffff166155e2565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600a6020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529083208054928701519192909169ffffffffffffffffffff6b010000000000000000000000909204821691161115610bcc57602086015186518591610bb4916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b10610bc457610bc48688876127b7565b5050506111e4565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101919091525b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a85016020908152604080832061ffff878116855290835281842082516101808101845281546affffffffffffffffffffff811682526b010000000000000000000000810469ffffffffffffffffffff1695820195909552750100000000000000000000000000000000000000000085048316938101939093527701000000000000000000000000000000000000000000000084048216606084015279010000000000000000000000000000000000000000000000000084049091166080830152909160a08301907b01000000000000000000000000000000000000000000000000000000900460ff166007811115610d2c57610d2c61551e565b6007811115610d3d57610d3d61551e565b815281547c0100000000000000000000000000000000000000000000000000000000900463ffffffff166020820152600182015473ffffffffffffffffffffffffffffffffffffffff8116604083015260609091019074010000000000000000000000000000000000000000900460ff166007811115610dbf57610dbf61551e565b6007811115610dd057610dd061551e565b8152600191909101547501000000000000000000000000000000000000000000810461ffff1660208084019190915277010000000000000000000000000000000000000000000000820460ff166040840152780100000000000000000000000000000000000000000000000090910467ffffffffffffffff1660609092019190915281810151908a015191925069ffffffffffffffffffff9081169116116111715760208101518851610e9e916affffffffffffffffffffff9091169069ffffffffffffffffffff1661279f565b5f03610ed057610eb785600a018a83606001515f612814565b5f8852610ec58989846128f1565b5050505050506111e4565b610edc88828b85612a0c565b805188516affffffffffffffffffffff9182169116111561103657805188518990610f089083906155e2565b6affffffffffffffffffffff169052505f81526060810151610f3090600a8701908b90612bd2565b608081015193507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61ffff85160161103157610f6d8989846128f1565b60ff16610140890152602088015188518791610fa2916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b10610fb257610fb2888a896127b7565b50505073ffffffffffffffffffffffffffffffffffffffff86165f908152600a909201602090815260408084206001855290915290912080547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000179055506111e4565b6111de565b805188516affffffffffffffffffffff91821691160361107b5761106285600a018a8360600151612bd2565b61107685600a018a83606001516001612814565b610eb7565b87518151829061108c9083906155e2565b6affffffffffffffffffffff908116909152825173ffffffffffffffffffffffffffffffffffffffff8c165f908152600a89016020818152604080842060608901805161ffff1686529252832080547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169490951693909317909355905161111693508c91612814565b6111316706f05b59d3b2000061112b8b612c2c565b90610790565b6020820151825161115d916affffffffffffffffffffff9091169069ffffffffffffffffffff1661279f565b1015610eb757610eb7898260600151612c6e565b61118485600a018a83606001515f612814565b61118f8989846128f1565b60ff166101408901526020880151885187916111c4916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b106111d4576111d4888a896127b7565b50505050506111e4565b50610c15565b50505050565b5f80807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848609848602925082811083820303915050805f0361123a5750670de0b6b3a7640000900490506106d5565b670de0b6b3a7640000811061128a576040517f5173648d00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044015b60405180910390fd5b5f670de0b6b3a7640000858709620400008185030493109091037d40000000000000000000000000000000000000000000000000000000000002919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f6106f283670de0b6b3a764000084612e42565b5f80611312846118dd565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600a602090815260408083206001845290915290205490915069ffffffffffffffffffff166113ee8261135e61079b565b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a60209081526040808320600184529091529020805463ffffffff9092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffff000000000000000000000090921669ffffffffffffffffffff90931692909217179055565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600c8401602090815260408083206001808552925290912054790100000000000000000000000000000000000000000000000000900461ffff161480156114b65773ffffffffffffffffffffffffffffffffffffffff86165f908152600885016020526040902080547fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000001790556118d5565b8183036114c557505050505050565b5f805b86518110156118a2578681815181106114e3576114e361560d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8a165f908152600c89018352604080822061ffff84168352909352919091205490925060019091019060ff7b010000000000000000000000000000000000000000000000000000009091041660068160078111156115645761156461551e565b14806115815750600781600781111561157f5761157f61551e565b145b8061159c57505f81600781111561159a5761159a61551e565b145b156115a757506114c8565b5073ffffffffffffffffffffffffffffffffffffffff88165f908152600c87016020908152604080832061ffff868116855292528083205475010000000000000000000000000000000000000000008104909216808452908320548b9369ffffffffffffffffffff6b010000000000000000000000948590048116949092049091169061163c8a670df27a2cdf448000610790565b8469ffffffffffffffffffff16111580156116635750898269ffffffffffffffffffff1610155b90505f8a8569ffffffffffffffffffff161015801561168d57508a8369ffffffffffffffffffff16105b90505f8b8669ffffffffffffffffffff161080156116ff575073ffffffffffffffffffffffffffffffffffffffff87165f908152600c8e016020908152604080832061ffff808e1685529252909120547901000000000000000000000000000000000000000000000000009004166001145b9050811561178c5750505073ffffffffffffffffffffffffffffffffffffffff9093165f908152600890990160205250506040909620805461ffff9093167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9093169290921790915550505050505050565b82156118175750505073ffffffffffffffffffffffffffffffffffffffff9093165f90815260089099016020526040909820805461ffff9099167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff90991698909817909755505050505050505050565b80156118965750505073ffffffffffffffffffffffffffffffffffffffff9093165f90815260089099016020525050604090962080547fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000001790555050505050505050565b505050505050506114c8565b506040517f053c10c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6002545f90819073ffffffffffffffffffffffffffffffffffffffff168161193f8573ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020546affffffffffffffffffffff1690565b69ffffffffffffffffffff1690505f805f8473ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611999573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119bd919061563a565b5073ffffffffffffffffffffffffffffffffffffffff808d165f90815260088c0160205260409020600201549497509295509350501680611a2a576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611a9f575f808413611a6b575f611a82565b611a82611a7d6402540be4008661568e565b612f49565b9050611a918686868685612f6f565b9a9950505050505050505050565b5f805f8373ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611aeb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b0f919061563a565b50935050925092505f611b2b87846112f390919063ffffffff16565b9050611b3b8489858a868b61324d565b9c9b505050505050505050505050565b5f805f5b8351811015611cac575f848281518110611b6b57611b6b61560d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff88165f9081528983526040808220835161ffff168352909352919091208054919250907b01000000000000000000000000000000000000000000000000000000900460ff166006816007811115611be557611be561551e565b1480611c0257506007816007811115611c0057611c0061551e565b145b15611c0f57505050611c9a565b602083015182547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1603611c4e5750505192506106f2915050565b84158015611c8c57506007600183015474010000000000000000000000000000000000000000900460ff166007811115611c8a57611c8a61551e565b145b15611c9657600194505b5050505b80611ca4816156d9565b915050611b4f565b508015611cbd5750600190506106f2565b6040517f0870870400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80611cfd868686866132e6565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff808616808652828552838620548b8501919091527901000000000000000000000000000000000000000000000000009004811660808b0181905260608b01516001808852939095529290942054949550909391927501000000000000000000000000000000000000000000909204909116908114611f6b5773ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff851684529091529020546101008801907b01000000000000000000000000000000000000000000000000000000900460ff166007811115611e0857611e0861551e565b90816007811115611e1b57611e1b61551e565b90525073ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff8086168552925290912054750100000000000000000000000000000000000000000090041660018114611eea5773ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832060018452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000061ffff841602179055611f57565b73ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832060018452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b5061ffff8116606088018190529150611fe2565b73ffffffffffffffffffffffffffffffffffffffff88165f9081526008860160205260409020805460019190601a90611fc79084907a010000000000000000000000000000000000000000000000000000900461ffff16615710565b92506101000a81548161ffff021916908361ffff1602179055505b73ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff8087168552908352928190208a518154938c0151928c015160608d015160808e01518716790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff91881677010000000000000000000000000000000000000000000000027fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff93909816750100000000000000000000000000000000000000000002929092167fffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9096166b010000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009097166affffffffffffffffffffff90941693909317959095179390931617939093179182168117835560a08a01518a9392909183917fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff9091167fffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffff909116177b010000000000000000000000000000000000000000000000000000008360078111156121d5576121d561551e565b021790555060c0820151815463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617815560e082015160018201805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178255610100850151927fffffffffffffffffffffff0000000000000000000000000000000000000000001617740100000000000000000000000000000000000000008360078111156122c7576122c761551e565b0217905550610120820151600191820180546101408501516101609095015167ffffffffffffffff1678010000000000000000000000000000000000000000000000000277ffffffffffffffffffffffffffffffffffffffffffffffff60ff90961677010000000000000000000000000000000000000000000000027fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff61ffff958616750100000000000000000000000000000000000000000002167fffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff909316929092179190911794909416939093179092559084161461243f57606087015173ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832061ffff808916855292529091208054919092167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff9091161790555b606087015173ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832061ffff808a1685529252909120805491909216790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90911617905560a087015160078111156124d5576124d561551e565b60e0880151606089015189516040805161ffff90931683526affffffffffffffffffffff909116602083015273ffffffffffffffffffffffffffffffffffffffff928316928c16917f464d814951bd2dcbcab73412ca99e1ec43435c5f9d3115eba8f8d475edf7dfcd910160405180910390a4505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f818152600860209081526040808320600a8352818420600180865290845282852054958552600c8452828520825461ffff780100000000000000000000000000000000000000000000000090910481168752945282852054818652928520549495919469ffffffffffffffffffff928316946b010000000000000000000000909404909216927901000000000000000000000000000000000000000000000000009092049091161480806126415750835461ffff7801000000000000000000000000000000000000000000000000909104166001145b156126b25782866020015169ffffffffffffffffffff16106126ad576060860151845461ffff9091167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9091161784555b612736565b81866020015169ffffffffffffffffffff161080156126e1575082866020015169ffffffffffffffffffff1610155b15612736576060860151845461ffff9091167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9091161784555b50505050505050565b5f80612758846affffffffffffffffffffff16846111ea565b90506affffffffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106f2836affffffffffffffffffffff16836111ea565b5f6127c58460a0015161348e565b905060058160078111156127db576127db61551e565b036127f0576127eb83858461090d565b6111e4565b60028160078111156128045761280461551e565b036111e4576111e483858461352c565b8015612827576127eb84846001856136b0565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff861680855281845282852080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000017905560018552925290912080547901000000000000000000000000000000000000000000000000009092027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905550505050565b5f806129008460a0015161348e565b90508360a0015160078111156129185761291861551e565b8460e0015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f1140e445b15f25d17f29961cc427972f8a2fe6c255e4a15a370e17620206fb828760600151875f015188602001516040516129ac9392919061ffff9390931683526affffffffffffffffffffff918216602084015216604082015260600190565b60405180910390a460058160078111156129c8576129c861551e565b036129e0576129d8858585613804565b9150506106f2565b60028160078111156129f4576129f461551e565b03612a0457612a04858585613ad9565b509392505050565b5f80845f01516affffffffffffffffffffff16865f01516affffffffffffffffffffff1611612a3c578551612a3f565b84515b90505f612a72826affffffffffffffffffffff16876020015169ffffffffffffffffffff16613c8f90919063ffffffff16565b9050612a8085878684613cec565b60058760a001516007811115612a9857612a9861551e565b03612af857612ad6612aae8861012001516106b2565b6020890151610a629069ffffffffffffffffffff166affffffffffffffffffffff8616613c8f565b84604001818151612ae7919061572b565b6affffffffffffffffffffff169052505b8184602001818151612b0a919061572b565b6affffffffffffffffffffff16905250835181908590612b2b90839061572b565b6affffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff8088165f908152600987016020908152604080832060e08d015190941683529290529081208054928616935091612b999084906cffffffffffffffffffffffffff1661574f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff16021790555050505050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260208481526040808320600184529091529020547501000000000000000000000000000000000000000000900461ffff166111e4848484846007613df1565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526008602052604081206001015481906064906107409060ff16670de0b6b3a7640000615578565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600a6020908152604080832061ffff85168452909152812080547b01000000000000000000000000000000000000000000000000000000900460ff166006816007811115612cd957612cd961551e565b1480612cf657506007816007811115612cf457612cf461551e565b145b15612d2d576040517f03f7cd9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f90815260088401602052604081205483547f010000000000000000000000000000000000000000000000000000000000000090910460ff169190612db2906affffffffffffffffffffff8116906b010000000000000000000000900469ffffffffffffffffffff1661273f565b5f8381526007870160209081526040808320600189015473ffffffffffffffffffffffffffffffffffffffff168452909152812080549293508392909190612e089084906affffffffffffffffffffff1661572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff16021790555061273685600a018888613ff2565b5f80807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709858702925082811083820303915050805f03612e9857838281612e8e57612e8e61558f565b04925050506106f2565b838110612ee2576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401611281565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f816ec097ce7bc90715b34b9f100000000081612f6857612f6861558f565b0492915050565b5f8069ffffffffffffffffffff86161580612f88575083155b80612f9257504284115b80612f9d57505f8513155b80612fb25750612faf84611c20615775565b42115b90505f878411612fcb57612fc684896155cf565b612fd5565b612fd588856155cf565b90505f8089118015612ff757506706f05b59d3b20000612ff5838b6112f3565b115b905082806130025750805b1561323d576040517f0b545214000000000000000000000000000000000000000000000000000000008152670de0b6b3a7640000600482015261070860248201525f903090630b54521490604401602060405180830381865afa15801561306b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061308f9190615788565b90505f620f42406130a883670de0b6b3a7640000615578565b6130b291906155bc565b90505f6130be82612f49565b9050815f036130f9576040517fca91730a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b851561310c579550613244945050505050565b5f8c82116131235761311e828e6155cf565b61312d565b61312d8d836155cf565b90508086116131455788975050505050505050613244565b6040517f70a082310000000000000000000000000000000000000000000000000000000081527388e6a0c2ddd26feeb64f039a2c41296fcb3f5640600482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2905f9082906370a0823190602401602060405180830381865afa1580156131c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131e79190615788565b905068056bc75e2d6310000081101561322c576040517f05f4eec200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b839950505050505050505050613244565b8493505050505b95945050505050565b5f69ffffffffffffffffffff87161580613265575082155b8061326f57504283115b8061327a57505f8513155b8061328f575069ffffffffffffffffffff8616155b80613298575081155b806132a257504282115b806132ad57505f8413155b90508015612736576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff8086168086529190935290832054790100000000000000000000000000000000000000000000000000900490911690158061334b575061ffff8116155b1561337e57613376868660018060ff16886020015169ffffffffffffffffffff168960a00151614177565b915050613486565b5f6133a1878786886020015169ffffffffffffffffffff16868a60a00151614276565b9050806133b2578392505050613486565b600181036133e6576133dd8787600185896020015169ffffffffffffffffffff168a60a00151614177565b92505050613486565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff80891685529083529220549087015160a0880151750100000000000000000000000000000000000000000090920490921691613480918a918a917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91869169ffffffffffffffffffff90911690614177565b93505050505b949350505050565b5f60018260078111156134a3576134a361551e565b14806134c0575060038260078111156134be576134be61551e565b145b156134cd57506001919050565b60028260078111156134e1576134e161551e565b14806134fe575060048260078111156134fc576134fc61551e565b145b1561350b57506002919050565b600582600781111561351f5761351f61551e565b0361090857506005919050565b5f8060048460a0015160078111156135465761354661551e565b03613552575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600b83016020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529220549086015169ffffffffffffffffffff6b0100000000000000000000009092048216911610806135df575061ffff81166001145b156135ed57600191506135fe565b6135fb83600b018786611b4b565b91505b61360d83600b01878785611cef565b845173ffffffffffffffffffffffffffffffffffffffff8088165f908152600986016020908152604080832060e08b0151909416835292905290812080546affffffffffffffffffffff909316929091906136789084906cffffffffffffffffffffffffff1661579f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff160217905550505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff8086168552925290912054790100000000000000000000000000000000000000000000000000900416600181146137835773ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8086168552925290912080549185167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff9092169190911790555b73ffffffffffffffffffffffffffffffffffffffff9093165f9081526020948552604080822061ffff948516835290955293909320805491909216790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90911617905550565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815260086020908152604080832080547f0100000000000000000000000000000000000000000000000000000000000000900460ff1680855260058452828520878401516007865284872060e08b015190981687529690945291842080549495869592949290919086906138a09084906affffffffffffffffffffff166155e2565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055508560400151865f018181516138e1919061572b565b6affffffffffffffffffffff90811690915288515f92501615613904575f613907565b60015b9050613955898960e00151838a5f01518b602001518960010160099054906101000a900467ffffffffffffffff16885f0160169054906101000a900469ffffffffffffffffffff165f6142f1565b60608801516002840180549298506affffffffffffffffffffff909116915f906139929084906fffffffffffffffffffffffffffffffff166157c5565b82546fffffffffffffffffffffffffffffffff9182166101009390930a9283029190920219909116179055508651825483905f906139de9084906affffffffffffffffffffff1661572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff160217905550865f0151845f01600d8282829054906101000a90046affffffffffffffffffffff16613a36919061572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff16021790555086602001516affffffffffffffffffffff16845f015f8282829054906101000a90046cffffffffffffffffffffffffff16613a9d919061574f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff16021790555050505050509392505050565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815260086020908152604080832054858301516009845282852060e08901519096168552949092528220805492937f010000000000000000000000000000000000000000000000000000000000000090920460ff16926affffffffffffffffffffff909216918490613b759084906cffffffffffffffffffffffffff1661579f565b82546cffffffffffffffffffffffffff9182166101009390930a92830291909202199091161790555082515f828152600784016020908152604080832060e089015173ffffffffffffffffffffffffffffffffffffffff16845290915281208054909190613bf19084906affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff81810219909316918316021790915560608501515f8481526005860160205260408120600201805492909316935090613c529084906fffffffffffffffffffffffffffffffff166157c5565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505050505050565b5f80613ca78469ffffffffffffffffffff16846111ea565b905069ffffffffffffffffffff8111156106f2576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808460c00151613cfb61079b565b613d0591906157ee565b9050621275008163ffffffff1611156118d5575f613d26620151808361580b565b613d369063ffffffff168561582d565b90508085606001818151613d4a919061572b565b6affffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff8981165f9081526008870160209081526040808320547f0100000000000000000000000000000000000000000000000000000000000000900460ff1680845260078a01835281842060e08e01519095168452939091529020805491935084929091600b91610acb9185916b0100000000000000000000009091041661572b565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff87168452909152902080548291907fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b01000000000000000000000000000000000000000000000000000000836007811115613e7857613e7861551e565b021790555073ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832060018085529252909120805461ffff8087167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff90921691909117909155831614613f7c5773ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8088168552925290912080549184167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff909216919091179055610839565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff87168452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260208481526040808320600184529091528082205461ffff84811680855283852080547901000000000000000000000000000000000000000000000000008082048516885286882080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff167501000000000000000000000000000000000000000000938490048716840217905582548281048616895296882080547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1697829004861690910296909617909555945292549190049091169060ff7b0100000000000000000000000000000000000000000000000000000090910416600781111561411a5761411a61551e565b60405161ffff8416815273ffffffffffffffffffffffffffffffffffffffff8516907f2097d5c49cf7ba09a2520b6b807629746c2527783ac92cac69d88e846c7f33f59060200160405180910390a36111e4848484846006613df1565b5f5b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff80891685529252822054790100000000000000000000000000000000000000000000000000900416906141d8898988888689614276565b036141e6578491505061426c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86036142625773ffffffffffffffffffffffffffffffffffffffff87165f9081526020898152604080832061ffff98891684529091529020547501000000000000000000000000000000000000000000900490941693614266565b8094505b50614179565b9695505050505050565b5f6142808261348e565b915060028260078111156142965761429661551e565b14806142b3575060058260078111156142b1576142b161551e565b145b156142cc576142c587878787876147e4565b905061426c565b60018260078111156142e0576142e061551e565b0361426c576142c58686868661490c565b5f8064ffffffffff8381161115614334576040517f3f6cc76800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61433f8b8b614a3f565b909350905060fe60ff8416116147c257604051806101800160405280896affffffffffffffffffffff168152602001886affffffffffffffffffffff1681526020018669ffffffffffffffffffff1681526020018a60028111156143a5576143a561551e565b81526001602082015260ff85811660408301528316606082015267ffffffffffffffff8816608082015260a0016143da61079b565b63ffffffff1681526020015f63ffffffff1681526020015f62ffffff1681526020018564ffffffffff1681525082600d015f8d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8560ff1660ff1681526020019081526020015f205f820151815f015f6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055506020820151815f01600b6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055506040820151815f0160166101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff1602179055506060820151816001015f6101000a81548160ff021916908360028111156145575761455761551e565b02179055506080820151600191909101805460a084015160c085015160e0860151610100808801516101208901516101408a0151610160909a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff90971660ff998a169093027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff16929092176201000095891695909502949094177fffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff166301000000938816939093027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff169290921764010000000067ffffffffffffffff90921691909102177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff938416027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff16177001000000000000000000000000000000009290911691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff909516949094027fffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffffff16939093177701000000000000000000000000000000000000000000000064ffffffffff9094169390930292909217909155604051908416815273ffffffffffffffffffffffffffffffffffffffff8b811691908d16907f098ddd45c8c2cfd19dab7bc996da0fe871e07f039af4def6686bb3115d87e4289060200160405180910390a36147d6565b60fe92506147d68b8b858c8c8c8c8c614d50565b505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8716845290915281205481906b010000000000000000000000900469ffffffffffffffffffff1684101580614845575061ffff85166001145b73ffffffffffffffffffffffffffffffffffffffff87165f9081526020898152604080832061ffff88168452909152812054919250906b010000000000000000000000900469ffffffffffffffffffff168510806148a7575061ffff84166001145b90508180156148b35750805b156148c2575f92505050613244565b816148f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92505050613244565b8061490157600192505050613244565b505095945050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600a6020908152604080832061ffff87168452909152812054819081906b010000000000000000000000900469ffffffffffffffffffff1685111580614971575061ffff86166001145b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a84016020908152604080832061ffff89168452909152812054919250906b010000000000000000000000900469ffffffffffffffffffff168611806149d7575061ffff85166001145b90508180156149e35750805b156149f3575f9350505050613486565b81614a23577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9350505050613486565b80614a345760019350505050613486565b505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f818152600d6020908152604080832094861680845294825280832060018452825280832093835260098252808320948352939052918220805483928392909174010000000000000000000000000000000000000000900460ff168303614b265780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001781556001820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff00ff1663010001001790555b60018083015460ff63010000008204811696506101009091041690811115614c0f5773ffffffffffffffffffffffffffffffffffffffff8089165f908152600d860160209081526040808320938b16835292815282822060ff80861684529152919020600190810154610100900490911690811115614bd7576001840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841602179055614c06565b6001840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b81965050614c8d565b815460ff7401000000000000000000000000000000000000000090910481169650861015614c845781546001908390601490614c6790849074010000000000000000000000000000000000000000900460ff1661585c565b92506101000a81548160ff021916908360ff160217905550614c8d565b50505050614d49565b600160ff86161115614d115773ffffffffffffffffffffffffffffffffffffffff8089165f908152600d860160209081526040808320938b16835292815282822060ff808a16845291529190206001018054918816610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff9092169190911790555b505060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff16630100000060ff861602179055505b9250929050565b5f8073ffffffffffffffffffffffffffffffffffffffff808b165f908152600d830160209081526040808320938d16835292815282822060ff8c1683529052209091506002600182015460ff166002811115614dae57614dae61551e565b03614ecc5760018101805467ffffffffffffffff8616640100000000027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff90911617905569ffffffffffffffffffff8316760100000000000000000000000000000000000000000000026affffffffffffffffffffff8087166b0100000000000000000000000275ffffffffffffffffffffff00000000000000000000001690881617178155614e5c61079b565b6001820180547fffffffffffffffffff0000000000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff93909316929092027fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff16919091179055614f2e565b5f614eee6affffffffffffffffffffff871667ffffffffffffffff871661279f565b90505f614f146affffffffffffffffffffff891669ffffffffffffffffffff871661279f565b9050614f2b8388848b85614f2661079b565b614f7f565b50505b6001808201805489927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836002811115614f6e57614f6e61551e565b021790555050505050505050505050565b60018601548654614fbf916b0100000000000000000000009091046affffffffffffffffffffff1690640100000000900467ffffffffffffffff1661279f565b614fc99085615775565b865490945085908790600b90614ffc9084906b01000000000000000000000090046affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff818102199093169183160217909155875461503f925086916b010000000000000000000000909104166151ba565b60018701805467ffffffffffffffff92909216640100000000027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff90921691909117905585546150c4906affffffffffffffffffffff81169069ffffffffffffffffffff7601000000000000000000000000000000000000000000009091041661279f565b6150ce9083615775565b8654909250839087905f906150f19084906affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff81810219909316918316021790915587546151249250849116615213565b865469ffffffffffffffffffff919091167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff9091161786556001909501805463ffffffff9096166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9096169590951790945550505050565b5f806151cf84670de0b6b3a764000085612e42565b905067ffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8061522884670de0b6b3a764000085612e42565b905069ffffffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b69ffffffffffffffffffff81168114615285575f80fd5b50565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040805190810167ffffffffffffffff811182821017156152d8576152d8615288565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561532557615325615288565b604052919050565b5f67ffffffffffffffff82111561534657615346615288565b5060051b60200190565b803561ffff81168114610908575f80fd5b5f82601f830112615370575f80fd5b813560206153856153808361532d565b6152de565b82815260069290921b840181019181810190868411156153a3575f80fd5b8286015b848110156153e857604081890312156153bf575f8081fd5b6153c76152b5565b6153d082615350565b815281850135858201528352918301916040016153a7565b509695505050505050565b5f82601f830112615402575f80fd5b813560206154126153808361532d565b82815260059290921b84018101918181019086841115615430575f80fd5b8286015b848110156153e85761544581615350565b8352918301918301615434565b5f805f805f8060c08789031215615467575f80fd5b863573ffffffffffffffffffffffffffffffffffffffff8116811461548a575f80fd5b9550602087013561549a8161526e565b945060408701356affffffffffffffffffffff811681146154b9575f80fd5b9350606087013567ffffffffffffffff808211156154d5575f80fd5b6154e18a838b01615361565b945060808901359150808211156154f6575f80fd5b5061550389828a016153f3565b92505061551260a08801615350565b90509295509295509295565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820281158282048414176106d5576106d561554b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826155ca576155ca61558f565b500490565b818103818111156106d5576106d561554b565b6affffffffffffffffffffff8281168282160390808211156156065761560661554b565b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f805f805f60a0868803121561564e575f80fd5b85516156598161526e565b8095505060208601519350604086015192506060860151915060808601516156808161526e565b809150509295509295909350565b8082025f82127f8000000000000000000000000000000000000000000000000000000000000000841416156156c5576156c561554b565b81810583148215176106d5576106d561554b565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157095761570961554b565b5060010190565b61ffff8181168382160190808211156156065761560661554b565b6affffffffffffffffffffff8181168382160190808211156156065761560661554b565b6cffffffffffffffffffffffffff8181168382160190808211156156065761560661554b565b808201808211156106d5576106d561554b565b5f60208284031215615798575f80fd5b5051919050565b6cffffffffffffffffffffffffff8281168282160390808211156156065761560661554b565b6fffffffffffffffffffffffffffffffff8181168382160190808211156156065761560661554b565b63ffffffff8281168282160390808211156156065761560661554b565b5f63ffffffff808416806158215761582161558f565b92169190910492915050565b6affffffffffffffffffffff8181168382160280821691908281146158545761585461554b565b505092915050565b60ff81811683821601908111156106d5576106d561554b56fea2646970667358221220116ec74834a7e2372eef2025cc77a42f0462ca31bffada0b0b43a1b15419d9d864736f6c63430008150033

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063f23811cc1461002d575b5f80fd5b61004061003b366004615452565b610042565b005b855f73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547e01000000000000000000000000000000000000000000000000000000000000900460ff16600181111561009f5761009f61551e565b146100d6576040517f0e27a3f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87165f9081526008602052604081205488917f010000000000000000000000000000000000000000000000000000000000000090910460ff16900361015c576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7c010000000000000000000000000000000000000000000000000000000090910460ff16016101db576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c02000000000000000000000000000000000000000000000000000000001790556040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915273ffffffffffffffffffffffffffffffffffffffff89165f90815260086020526040812090610281866106b2565b825490915061ffff8088167c01000000000000000000000000000000000000000000000000000000009092041611806102c2575067d02ab486cedc00008110155b156102f9576040517f56cd57b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61031c69ffffffffffffffffffff8b166affffffffffffffffffffff8b166106db565b602084015261032a8b6106f9565b60608401526103388b61074a565b604084018190526affffffffffffffffffffff8a161080610360575082606001518360200151105b15610397576040517faacd1fa000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208301516103a69082610790565b82547f0100000000000000000000000000000000000000000000000000000000000000900460ff165f9081526007602090815260408083203384529091529020546affffffffffffffffffffff16101561042c576040517f7d243a2c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610180810182525f9181018290526080810182905260c081018290526101008101829052610120810182905261014081018290526101608101919091523360e082015269ffffffffffffffffffff8b1660208201526affffffffffffffffffffff8a16815282547a010000000000000000000000000000000000000000000000000000900461ffff166060820152600560a08201526104cd61079b565b63ffffffff1660c082015261ffff80881661012083015273ffffffffffffffffffffffffffffffffffffffff8d165f818152600a6020818152604080842060018552808352818520547901000000000000000000000000000000000000000000000000009004871660808c019081529585529282529351909416825283522090820151815469ffffffffffffffffffff9182166b010000000000000000000000909104909116108015906105b55750600181547b01000000000000000000000000000000000000000000000000000000900460ff1660078111156105b3576105b361551e565b145b15610615576106158d6106028f73ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020546affffffffffffffffffffff1690565b69ffffffffffffffffffff16848c6107af565b61061e8d610840565b60a08601819052602083015169ffffffffffffffffffff16101561064c576106478d838c61090d565b61065c565b61065c8d838c8860600151610b01565b5050600280547fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c01000000000000000000000000000000000000000000000000000000001790555050505050505050505050565b5f60646106cb61ffff8416670de0b6b3a7640000615578565b6106d591906155bc565b92915050565b5f6106f28369ffffffffffffffffffff16836111ea565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260086020526040812060010154819060649061074090610100900460ff16670de0b6b3a7640000615578565b6106f291906155bc565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526008602052604081206001015481906106f29062010000900461ffff16670de0b6b3a7640000615578565b5f6106f283836111ea565b5f6107aa6362f6fc65426155cf565b905090565b5f83836020015169ffffffffffffffffffff16106107fd576611c37937e080006107f58586866020015169ffffffffffffffffffff166107ef91906155cf565b906112f3565b119050610829565b6611c37937e0800061082585856020015169ffffffffffffffffffff16876107ef91906155cf565b1190505b8015610839576108398583611307565b5050505050565b5f6103846108a18373ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690565b6108a961079b565b63ffffffff166108b991906155cf565b10156108ff575073ffffffffffffffffffffffffffffffffffffffff165f908152600a602090815260408083206001845290915290205469ffffffffffffffffffff1690565b6106d5826118dd565b919050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600c6020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529083205491850151839269ffffffffffffffffffff6b01000000000000000000000090910481169116108061099b575061ffff81166001145b156109a957600191506109ba565b6109b783600c018786611b4b565b91505b6109c983600c01878785611cef565b6109d38686612553565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526008840160205260408120546101208701517f010000000000000000000000000000000000000000000000000000000000000090910460ff169190610a7590610a36906106b2565b60208901518951610a62916affffffffffffffffffffff9091169069ffffffffffffffffffff1661273f565b6affffffffffffffffffffff169061273f565b5f838152600787016020908152604080832060e08c015173ffffffffffffffffffffffffffffffffffffffff168452909152812080549293508392909190610acb9084906affffffffffffffffffffff166155e2565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600a6020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529083208054928701519192909169ffffffffffffffffffff6b010000000000000000000000909204821691161115610bcc57602086015186518591610bb4916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b10610bc457610bc48688876127b7565b5050506111e4565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101919091525b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a85016020908152604080832061ffff878116855290835281842082516101808101845281546affffffffffffffffffffff811682526b010000000000000000000000810469ffffffffffffffffffff1695820195909552750100000000000000000000000000000000000000000085048316938101939093527701000000000000000000000000000000000000000000000084048216606084015279010000000000000000000000000000000000000000000000000084049091166080830152909160a08301907b01000000000000000000000000000000000000000000000000000000900460ff166007811115610d2c57610d2c61551e565b6007811115610d3d57610d3d61551e565b815281547c0100000000000000000000000000000000000000000000000000000000900463ffffffff166020820152600182015473ffffffffffffffffffffffffffffffffffffffff8116604083015260609091019074010000000000000000000000000000000000000000900460ff166007811115610dbf57610dbf61551e565b6007811115610dd057610dd061551e565b8152600191909101547501000000000000000000000000000000000000000000810461ffff1660208084019190915277010000000000000000000000000000000000000000000000820460ff166040840152780100000000000000000000000000000000000000000000000090910467ffffffffffffffff1660609092019190915281810151908a015191925069ffffffffffffffffffff9081169116116111715760208101518851610e9e916affffffffffffffffffffff9091169069ffffffffffffffffffff1661279f565b5f03610ed057610eb785600a018a83606001515f612814565b5f8852610ec58989846128f1565b5050505050506111e4565b610edc88828b85612a0c565b805188516affffffffffffffffffffff9182169116111561103657805188518990610f089083906155e2565b6affffffffffffffffffffff169052505f81526060810151610f3090600a8701908b90612bd2565b608081015193507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61ffff85160161103157610f6d8989846128f1565b60ff16610140890152602088015188518791610fa2916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b10610fb257610fb2888a896127b7565b50505073ffffffffffffffffffffffffffffffffffffffff86165f908152600a909201602090815260408084206001855290915290912080547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff16790100000000000000000000000000000000000000000000000000179055506111e4565b6111de565b805188516affffffffffffffffffffff91821691160361107b5761106285600a018a8360600151612bd2565b61107685600a018a83606001516001612814565b610eb7565b87518151829061108c9083906155e2565b6affffffffffffffffffffff908116909152825173ffffffffffffffffffffffffffffffffffffffff8c165f908152600a89016020818152604080842060608901805161ffff1686529252832080547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169490951693909317909355905161111693508c91612814565b6111316706f05b59d3b2000061112b8b612c2c565b90610790565b6020820151825161115d916affffffffffffffffffffff9091169069ffffffffffffffffffff1661279f565b1015610eb757610eb7898260600151612c6e565b61118485600a018a83606001515f612814565b61118f8989846128f1565b60ff166101408901526020880151885187916111c4916affffffffffffffffffffff169069ffffffffffffffffffff1661279f565b106111d4576111d4888a896127b7565b50505050506111e4565b50610c15565b50505050565b5f80807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848609848602925082811083820303915050805f0361123a5750670de0b6b3a7640000900490506106d5565b670de0b6b3a7640000811061128a576040517f5173648d00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044015b60405180910390fd5b5f670de0b6b3a7640000858709620400008185030493109091037d40000000000000000000000000000000000000000000000000000000000002919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f6106f283670de0b6b3a764000084612e42565b5f80611312846118dd565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600a602090815260408083206001845290915290205490915069ffffffffffffffffffff166113ee8261135e61079b565b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a60209081526040808320600184529091529020805463ffffffff9092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffff000000000000000000000090921669ffffffffffffffffffff90931692909217179055565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600c8401602090815260408083206001808552925290912054790100000000000000000000000000000000000000000000000000900461ffff161480156114b65773ffffffffffffffffffffffffffffffffffffffff86165f908152600885016020526040902080547fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000001790556118d5565b8183036114c557505050505050565b5f805b86518110156118a2578681815181106114e3576114e361560d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8a165f908152600c89018352604080822061ffff84168352909352919091205490925060019091019060ff7b010000000000000000000000000000000000000000000000000000009091041660068160078111156115645761156461551e565b14806115815750600781600781111561157f5761157f61551e565b145b8061159c57505f81600781111561159a5761159a61551e565b145b156115a757506114c8565b5073ffffffffffffffffffffffffffffffffffffffff88165f908152600c87016020908152604080832061ffff868116855292528083205475010000000000000000000000000000000000000000008104909216808452908320548b9369ffffffffffffffffffff6b010000000000000000000000948590048116949092049091169061163c8a670df27a2cdf448000610790565b8469ffffffffffffffffffff16111580156116635750898269ffffffffffffffffffff1610155b90505f8a8569ffffffffffffffffffff161015801561168d57508a8369ffffffffffffffffffff16105b90505f8b8669ffffffffffffffffffff161080156116ff575073ffffffffffffffffffffffffffffffffffffffff87165f908152600c8e016020908152604080832061ffff808e1685529252909120547901000000000000000000000000000000000000000000000000009004166001145b9050811561178c5750505073ffffffffffffffffffffffffffffffffffffffff9093165f908152600890990160205250506040909620805461ffff9093167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9093169290921790915550505050505050565b82156118175750505073ffffffffffffffffffffffffffffffffffffffff9093165f90815260089099016020526040909820805461ffff9099167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff90991698909817909755505050505050505050565b80156118965750505073ffffffffffffffffffffffffffffffffffffffff9093165f90815260089099016020525050604090962080547fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000001790555050505050505050565b505050505050506114c8565b506040517f053c10c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6002545f90819073ffffffffffffffffffffffffffffffffffffffff168161193f8573ffffffffffffffffffffffffffffffffffffffff165f908152600a60209081526040808320600184529091529020546affffffffffffffffffffff1690565b69ffffffffffffffffffff1690505f805f8473ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611999573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119bd919061563a565b5073ffffffffffffffffffffffffffffffffffffffff808d165f90815260088c0160205260409020600201549497509295509350501680611a2a576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611a9f575f808413611a6b575f611a82565b611a82611a7d6402540be4008661568e565b612f49565b9050611a918686868685612f6f565b9a9950505050505050505050565b5f805f8373ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611aeb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b0f919061563a565b50935050925092505f611b2b87846112f390919063ffffffff16565b9050611b3b8489858a868b61324d565b9c9b505050505050505050505050565b5f805f5b8351811015611cac575f848281518110611b6b57611b6b61560d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff88165f9081528983526040808220835161ffff168352909352919091208054919250907b01000000000000000000000000000000000000000000000000000000900460ff166006816007811115611be557611be561551e565b1480611c0257506007816007811115611c0057611c0061551e565b145b15611c0f57505050611c9a565b602083015182547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1603611c4e5750505192506106f2915050565b84158015611c8c57506007600183015474010000000000000000000000000000000000000000900460ff166007811115611c8a57611c8a61551e565b145b15611c9657600194505b5050505b80611ca4816156d9565b915050611b4f565b508015611cbd5750600190506106f2565b6040517f0870870400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80611cfd868686866132e6565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff808616808652828552838620548b8501919091527901000000000000000000000000000000000000000000000000009004811660808b0181905260608b01516001808852939095529290942054949550909391927501000000000000000000000000000000000000000000909204909116908114611f6b5773ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff851684529091529020546101008801907b01000000000000000000000000000000000000000000000000000000900460ff166007811115611e0857611e0861551e565b90816007811115611e1b57611e1b61551e565b90525073ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff8086168552925290912054750100000000000000000000000000000000000000000090041660018114611eea5773ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832060018452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000061ffff841602179055611f57565b73ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832060018452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b5061ffff8116606088018190529150611fe2565b73ffffffffffffffffffffffffffffffffffffffff88165f9081526008860160205260409020805460019190601a90611fc79084907a010000000000000000000000000000000000000000000000000000900461ffff16615710565b92506101000a81548161ffff021916908361ffff1602179055505b73ffffffffffffffffffffffffffffffffffffffff88165f90815260208a8152604080832061ffff8087168552908352928190208a518154938c0151928c015160608d015160808e01518716790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff91881677010000000000000000000000000000000000000000000000027fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff93909816750100000000000000000000000000000000000000000002929092167fffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffff69ffffffffffffffffffff9096166b010000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009097166affffffffffffffffffffff90941693909317959095179390931617939093179182168117835560a08a01518a9392909183917fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff9091167fffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffff909116177b010000000000000000000000000000000000000000000000000000008360078111156121d5576121d561551e565b021790555060c0820151815463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617815560e082015160018201805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178255610100850151927fffffffffffffffffffffff0000000000000000000000000000000000000000001617740100000000000000000000000000000000000000008360078111156122c7576122c761551e565b0217905550610120820151600191820180546101408501516101609095015167ffffffffffffffff1678010000000000000000000000000000000000000000000000000277ffffffffffffffffffffffffffffffffffffffffffffffff60ff90961677010000000000000000000000000000000000000000000000027fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff61ffff958616750100000000000000000000000000000000000000000002167fffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffff909316929092179190911794909416939093179092559084161461243f57606087015173ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832061ffff808916855292529091208054919092167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff9091161790555b606087015173ffffffffffffffffffffffffffffffffffffffff89165f90815260208b8152604080832061ffff808a1685529252909120805491909216790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90911617905560a087015160078111156124d5576124d561551e565b60e0880151606089015189516040805161ffff90931683526affffffffffffffffffffff909116602083015273ffffffffffffffffffffffffffffffffffffffff928316928c16917f464d814951bd2dcbcab73412ca99e1ec43435c5f9d3115eba8f8d475edf7dfcd910160405180910390a4505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f818152600860209081526040808320600a8352818420600180865290845282852054958552600c8452828520825461ffff780100000000000000000000000000000000000000000000000090910481168752945282852054818652928520549495919469ffffffffffffffffffff928316946b010000000000000000000000909404909216927901000000000000000000000000000000000000000000000000009092049091161480806126415750835461ffff7801000000000000000000000000000000000000000000000000909104166001145b156126b25782866020015169ffffffffffffffffffff16106126ad576060860151845461ffff9091167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9091161784555b612736565b81866020015169ffffffffffffffffffff161080156126e1575082866020015169ffffffffffffffffffff1610155b15612736576060860151845461ffff9091167801000000000000000000000000000000000000000000000000027fffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffff9091161784555b50505050505050565b5f80612758846affffffffffffffffffffff16846111ea565b90506affffffffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106f2836affffffffffffffffffffff16836111ea565b5f6127c58460a0015161348e565b905060058160078111156127db576127db61551e565b036127f0576127eb83858461090d565b6111e4565b60028160078111156128045761280461551e565b036111e4576111e483858461352c565b8015612827576127eb84846001856136b0565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff861680855281845282852080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000017905560018552925290912080547901000000000000000000000000000000000000000000000000009092027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905550505050565b5f806129008460a0015161348e565b90508360a0015160078111156129185761291861551e565b8460e0015173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f1140e445b15f25d17f29961cc427972f8a2fe6c255e4a15a370e17620206fb828760600151875f015188602001516040516129ac9392919061ffff9390931683526affffffffffffffffffffff918216602084015216604082015260600190565b60405180910390a460058160078111156129c8576129c861551e565b036129e0576129d8858585613804565b9150506106f2565b60028160078111156129f4576129f461551e565b03612a0457612a04858585613ad9565b509392505050565b5f80845f01516affffffffffffffffffffff16865f01516affffffffffffffffffffff1611612a3c578551612a3f565b84515b90505f612a72826affffffffffffffffffffff16876020015169ffffffffffffffffffff16613c8f90919063ffffffff16565b9050612a8085878684613cec565b60058760a001516007811115612a9857612a9861551e565b03612af857612ad6612aae8861012001516106b2565b6020890151610a629069ffffffffffffffffffff166affffffffffffffffffffff8616613c8f565b84604001818151612ae7919061572b565b6affffffffffffffffffffff169052505b8184602001818151612b0a919061572b565b6affffffffffffffffffffff16905250835181908590612b2b90839061572b565b6affffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff8088165f908152600987016020908152604080832060e08d015190941683529290529081208054928616935091612b999084906cffffffffffffffffffffffffff1661574f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff16021790555050505050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260208481526040808320600184529091529020547501000000000000000000000000000000000000000000900461ffff166111e4848484846007613df1565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526008602052604081206001015481906064906107409060ff16670de0b6b3a7640000615578565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600a6020908152604080832061ffff85168452909152812080547b01000000000000000000000000000000000000000000000000000000900460ff166006816007811115612cd957612cd961551e565b1480612cf657506007816007811115612cf457612cf461551e565b145b15612d2d576040517f03f7cd9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f90815260088401602052604081205483547f010000000000000000000000000000000000000000000000000000000000000090910460ff169190612db2906affffffffffffffffffffff8116906b010000000000000000000000900469ffffffffffffffffffff1661273f565b5f8381526007870160209081526040808320600189015473ffffffffffffffffffffffffffffffffffffffff168452909152812080549293508392909190612e089084906affffffffffffffffffffff1661572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff16021790555061273685600a018888613ff2565b5f80807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709858702925082811083820303915050805f03612e9857838281612e8e57612e8e61558f565b04925050506106f2565b838110612ee2576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401611281565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f816ec097ce7bc90715b34b9f100000000081612f6857612f6861558f565b0492915050565b5f8069ffffffffffffffffffff86161580612f88575083155b80612f9257504284115b80612f9d57505f8513155b80612fb25750612faf84611c20615775565b42115b90505f878411612fcb57612fc684896155cf565b612fd5565b612fd588856155cf565b90505f8089118015612ff757506706f05b59d3b20000612ff5838b6112f3565b115b905082806130025750805b1561323d576040517f0b545214000000000000000000000000000000000000000000000000000000008152670de0b6b3a7640000600482015261070860248201525f903090630b54521490604401602060405180830381865afa15801561306b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061308f9190615788565b90505f620f42406130a883670de0b6b3a7640000615578565b6130b291906155bc565b90505f6130be82612f49565b9050815f036130f9576040517fca91730a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b851561310c579550613244945050505050565b5f8c82116131235761311e828e6155cf565b61312d565b61312d8d836155cf565b90508086116131455788975050505050505050613244565b6040517f70a082310000000000000000000000000000000000000000000000000000000081527388e6a0c2ddd26feeb64f039a2c41296fcb3f5640600482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2905f9082906370a0823190602401602060405180830381865afa1580156131c3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131e79190615788565b905068056bc75e2d6310000081101561322c576040517f05f4eec200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b839950505050505050505050613244565b8493505050505b95945050505050565b5f69ffffffffffffffffffff87161580613265575082155b8061326f57504283115b8061327a57505f8513155b8061328f575069ffffffffffffffffffff8616155b80613298575081155b806132a257504282115b806132ad57505f8413155b90508015612736576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff8086168086529190935290832054790100000000000000000000000000000000000000000000000000900490911690158061334b575061ffff8116155b1561337e57613376868660018060ff16886020015169ffffffffffffffffffff168960a00151614177565b915050613486565b5f6133a1878786886020015169ffffffffffffffffffff16868a60a00151614276565b9050806133b2578392505050613486565b600181036133e6576133dd8787600185896020015169ffffffffffffffffffff168a60a00151614177565b92505050613486565b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff80891685529083529220549087015160a0880151750100000000000000000000000000000000000000000090920490921691613480918a918a917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91869169ffffffffffffffffffff90911690614177565b93505050505b949350505050565b5f60018260078111156134a3576134a361551e565b14806134c0575060038260078111156134be576134be61551e565b145b156134cd57506001919050565b60028260078111156134e1576134e161551e565b14806134fe575060048260078111156134fc576134fc61551e565b145b1561350b57506002919050565b600582600781111561351f5761351f61551e565b0361090857506005919050565b5f8060048460a0015160078111156135465761354661551e565b03613552575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600b83016020908152604080832060018452825280832054790100000000000000000000000000000000000000000000000000900461ffff168084529220549086015169ffffffffffffffffffff6b0100000000000000000000009092048216911610806135df575061ffff81166001145b156135ed57600191506135fe565b6135fb83600b018786611b4b565b91505b61360d83600b01878785611cef565b845173ffffffffffffffffffffffffffffffffffffffff8088165f908152600986016020908152604080832060e08b0151909416835292905290812080546affffffffffffffffffffff909316929091906136789084906cffffffffffffffffffffffffff1661579f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff160217905550505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526020858152604080832061ffff8086168552925290912054790100000000000000000000000000000000000000000000000000900416600181146137835773ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8086168552925290912080549185167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff9092169190911790555b73ffffffffffffffffffffffffffffffffffffffff9093165f9081526020948552604080822061ffff948516835290955293909320805491909216790100000000000000000000000000000000000000000000000000027fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff90911617905550565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815260086020908152604080832080547f0100000000000000000000000000000000000000000000000000000000000000900460ff1680855260058452828520878401516007865284872060e08b015190981687529690945291842080549495869592949290919086906138a09084906affffffffffffffffffffff166155e2565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055508560400151865f018181516138e1919061572b565b6affffffffffffffffffffff90811690915288515f92501615613904575f613907565b60015b9050613955898960e00151838a5f01518b602001518960010160099054906101000a900467ffffffffffffffff16885f0160169054906101000a900469ffffffffffffffffffff165f6142f1565b60608801516002840180549298506affffffffffffffffffffff909116915f906139929084906fffffffffffffffffffffffffffffffff166157c5565b82546fffffffffffffffffffffffffffffffff9182166101009390930a9283029190920219909116179055508651825483905f906139de9084906affffffffffffffffffffff1661572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff160217905550865f0151845f01600d8282829054906101000a90046affffffffffffffffffffff16613a36919061572b565b92506101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff16021790555086602001516affffffffffffffffffffff16845f015f8282829054906101000a90046cffffffffffffffffffffffffff16613a9d919061574f565b92506101000a8154816cffffffffffffffffffffffffff02191690836cffffffffffffffffffffffffff16021790555050505050509392505050565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815260086020908152604080832054858301516009845282852060e08901519096168552949092528220805492937f010000000000000000000000000000000000000000000000000000000000000090920460ff16926affffffffffffffffffffff909216918490613b759084906cffffffffffffffffffffffffff1661579f565b82546cffffffffffffffffffffffffff9182166101009390930a92830291909202199091161790555082515f828152600784016020908152604080832060e089015173ffffffffffffffffffffffffffffffffffffffff16845290915281208054909190613bf19084906affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff81810219909316918316021790915560608501515f8481526005860160205260408120600201805492909316935090613c529084906fffffffffffffffffffffffffffffffff166157c5565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505050505050565b5f80613ca78469ffffffffffffffffffff16846111ea565b905069ffffffffffffffffffff8111156106f2576040517ebfc92100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808460c00151613cfb61079b565b613d0591906157ee565b9050621275008163ffffffff1611156118d5575f613d26620151808361580b565b613d369063ffffffff168561582d565b90508085606001818151613d4a919061572b565b6affffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff8981165f9081526008870160209081526040808320547f0100000000000000000000000000000000000000000000000000000000000000900460ff1680845260078a01835281842060e08e01519095168452939091529020805491935084929091600b91610acb9185916b0100000000000000000000009091041661572b565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff87168452909152902080548291907fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b01000000000000000000000000000000000000000000000000000000836007811115613e7857613e7861551e565b021790555073ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832060018085529252909120805461ffff8087167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff90921691909117909155831614613f7c5773ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8088168552925290912080549184167501000000000000000000000000000000000000000000027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff909216919091179055610839565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff87168452909152902080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260208481526040808320600184529091528082205461ffff84811680855283852080547901000000000000000000000000000000000000000000000000008082048516885286882080547fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff167501000000000000000000000000000000000000000000938490048716840217905582548281048616895296882080547fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff1697829004861690910296909617909555945292549190049091169060ff7b0100000000000000000000000000000000000000000000000000000090910416600781111561411a5761411a61551e565b60405161ffff8416815273ffffffffffffffffffffffffffffffffffffffff8516907f2097d5c49cf7ba09a2520b6b807629746c2527783ac92cac69d88e846c7f33f59060200160405180910390a36111e4848484846006613df1565b5f5b73ffffffffffffffffffffffffffffffffffffffff86165f9081526020888152604080832061ffff80891685529252822054790100000000000000000000000000000000000000000000000000900416906141d8898988888689614276565b036141e6578491505061426c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86036142625773ffffffffffffffffffffffffffffffffffffffff87165f9081526020898152604080832061ffff98891684529091529020547501000000000000000000000000000000000000000000900490941693614266565b8094505b50614179565b9695505050505050565b5f6142808261348e565b915060028260078111156142965761429661551e565b14806142b3575060058260078111156142b1576142b161551e565b145b156142cc576142c587878787876147e4565b905061426c565b60018260078111156142e0576142e061551e565b0361426c576142c58686868661490c565b5f8064ffffffffff8381161115614334576040517f3f6cc76800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61433f8b8b614a3f565b909350905060fe60ff8416116147c257604051806101800160405280896affffffffffffffffffffff168152602001886affffffffffffffffffffff1681526020018669ffffffffffffffffffff1681526020018a60028111156143a5576143a561551e565b81526001602082015260ff85811660408301528316606082015267ffffffffffffffff8816608082015260a0016143da61079b565b63ffffffff1681526020015f63ffffffff1681526020015f62ffffff1681526020018564ffffffffff1681525082600d015f8d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8560ff1660ff1681526020019081526020015f205f820151815f015f6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055506020820151815f01600b6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff1602179055506040820151815f0160166101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff1602179055506060820151816001015f6101000a81548160ff021916908360028111156145575761455761551e565b02179055506080820151600191909101805460a084015160c085015160e0860151610100808801516101208901516101408a0151610160909a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff90971660ff998a169093027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff16929092176201000095891695909502949094177fffffffffffffffffffffffffffffffffffffffff000000000000000000ffffff166301000000938816939093027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff169290921764010000000067ffffffffffffffff90921691909102177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff938416027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff16177001000000000000000000000000000000009290911691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000062ffffff909516949094027fffffffff0000000000ffffffffffffffffffffffffffffffffffffffffffffff16939093177701000000000000000000000000000000000000000000000064ffffffffff9094169390930292909217909155604051908416815273ffffffffffffffffffffffffffffffffffffffff8b811691908d16907f098ddd45c8c2cfd19dab7bc996da0fe871e07f039af4def6686bb3115d87e4289060200160405180910390a36147d6565b60fe92506147d68b8b858c8c8c8c8c614d50565b505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020868152604080832061ffff8716845290915281205481906b010000000000000000000000900469ffffffffffffffffffff1684101580614845575061ffff85166001145b73ffffffffffffffffffffffffffffffffffffffff87165f9081526020898152604080832061ffff88168452909152812054919250906b010000000000000000000000900469ffffffffffffffffffff168510806148a7575061ffff84166001145b90508180156148b35750805b156148c2575f92505050613244565b816148f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92505050613244565b8061490157600192505050613244565b505095945050505050565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600a6020908152604080832061ffff87168452909152812054819081906b010000000000000000000000900469ffffffffffffffffffff1685111580614971575061ffff86166001145b73ffffffffffffffffffffffffffffffffffffffff88165f908152600a84016020908152604080832061ffff89168452909152812054919250906b010000000000000000000000900469ffffffffffffffffffff168611806149d7575061ffff85166001145b90508180156149e35750805b156149f3575f9350505050613486565b81614a23577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9350505050613486565b80614a345760019350505050613486565b505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f818152600d6020908152604080832094861680845294825280832060018452825280832093835260098252808320948352939052918220805483928392909174010000000000000000000000000000000000000000900460ff168303614b265780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001781556001820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff00ff1663010001001790555b60018083015460ff63010000008204811696506101009091041690811115614c0f5773ffffffffffffffffffffffffffffffffffffffff8089165f908152600d860160209081526040808320938b16835292815282822060ff80861684529152919020600190810154610100900490911690811115614bd7576001840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841602179055614c06565b6001840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b81965050614c8d565b815460ff7401000000000000000000000000000000000000000090910481169650861015614c845781546001908390601490614c6790849074010000000000000000000000000000000000000000900460ff1661585c565b92506101000a81548160ff021916908360ff160217905550614c8d565b50505050614d49565b600160ff86161115614d115773ffffffffffffffffffffffffffffffffffffffff8089165f908152600d860160209081526040808320938b16835292815282822060ff808a16845291529190206001018054918816610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff9092169190911790555b505060010180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff16630100000060ff861602179055505b9250929050565b5f8073ffffffffffffffffffffffffffffffffffffffff808b165f908152600d830160209081526040808320938d16835292815282822060ff8c1683529052209091506002600182015460ff166002811115614dae57614dae61551e565b03614ecc5760018101805467ffffffffffffffff8616640100000000027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff90911617905569ffffffffffffffffffff8316760100000000000000000000000000000000000000000000026affffffffffffffffffffff8087166b0100000000000000000000000275ffffffffffffffffffffff00000000000000000000001690881617178155614e5c61079b565b6001820180547fffffffffffffffffff0000000000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff93909316929092027fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff16919091179055614f2e565b5f614eee6affffffffffffffffffffff871667ffffffffffffffff871661279f565b90505f614f146affffffffffffffffffffff891669ffffffffffffffffffff871661279f565b9050614f2b8388848b85614f2661079b565b614f7f565b50505b6001808201805489927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836002811115614f6e57614f6e61551e565b021790555050505050505050505050565b60018601548654614fbf916b0100000000000000000000009091046affffffffffffffffffffff1690640100000000900467ffffffffffffffff1661279f565b614fc99085615775565b865490945085908790600b90614ffc9084906b01000000000000000000000090046affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff818102199093169183160217909155875461503f925086916b010000000000000000000000909104166151ba565b60018701805467ffffffffffffffff92909216640100000000027fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff90921691909117905585546150c4906affffffffffffffffffffff81169069ffffffffffffffffffff7601000000000000000000000000000000000000000000009091041661279f565b6150ce9083615775565b8654909250839087905f906150f19084906affffffffffffffffffffff1661572b565b82546101009290920a6affffffffffffffffffffff81810219909316918316021790915587546151249250849116615213565b865469ffffffffffffffffffff919091167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff9091161786556001909501805463ffffffff9096166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9096169590951790945550505050565b5f806151cf84670de0b6b3a764000085612e42565b905067ffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8061522884670de0b6b3a764000085612e42565b905069ffffffffffffffffffff8111156106f2576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b69ffffffffffffffffffff81168114615285575f80fd5b50565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040805190810167ffffffffffffffff811182821017156152d8576152d8615288565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561532557615325615288565b604052919050565b5f67ffffffffffffffff82111561534657615346615288565b5060051b60200190565b803561ffff81168114610908575f80fd5b5f82601f830112615370575f80fd5b813560206153856153808361532d565b6152de565b82815260069290921b840181019181810190868411156153a3575f80fd5b8286015b848110156153e857604081890312156153bf575f8081fd5b6153c76152b5565b6153d082615350565b815281850135858201528352918301916040016153a7565b509695505050505050565b5f82601f830112615402575f80fd5b813560206154126153808361532d565b82815260059290921b84018101918181019086841115615430575f80fd5b8286015b848110156153e85761544581615350565b8352918301918301615434565b5f805f805f8060c08789031215615467575f80fd5b863573ffffffffffffffffffffffffffffffffffffffff8116811461548a575f80fd5b9550602087013561549a8161526e565b945060408701356affffffffffffffffffffff811681146154b9575f80fd5b9350606087013567ffffffffffffffff808211156154d5575f80fd5b6154e18a838b01615361565b945060808901359150808211156154f6575f80fd5b5061550389828a016153f3565b92505061551260a08801615350565b90509295509295509295565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820281158282048414176106d5576106d561554b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826155ca576155ca61558f565b500490565b818103818111156106d5576106d561554b565b6affffffffffffffffffffff8281168282160390808211156156065761560661554b565b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f805f805f60a0868803121561564e575f80fd5b85516156598161526e565b8095505060208601519350604086015192506060860151915060808601516156808161526e565b809150509295509295909350565b8082025f82127f8000000000000000000000000000000000000000000000000000000000000000841416156156c5576156c561554b565b81810583148215176106d5576106d561554b565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157095761570961554b565b5060010190565b61ffff8181168382160190808211156156065761560661554b565b6affffffffffffffffffffff8181168382160190808211156156065761560661554b565b6cffffffffffffffffffffffffff8181168382160190808211156156065761560661554b565b808201808211156106d5576106d561554b565b5f60208284031215615798575f80fd5b5051919050565b6cffffffffffffffffffffffffff8281168282160390808211156156065761560661554b565b6fffffffffffffffffffffffffffffffff8181168382160190808211156156065761560661554b565b63ffffffff8281168282160390808211156156065761560661554b565b5f63ffffffff808416806158215761582161558f565b92169190910492915050565b6affffffffffffffffffffff8181168382160280821691908281146158545761585461554b565b505092915050565b60ff81811683821601908111156106d5576106d561554b56fea2646970667358221220116ec74834a7e2372eef2025cc77a42f0462ca31bffada0b0b43a1b15419d9d864736f6c63430008150033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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
Loading...
Loading
[ 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.