ETH Price: $2,318.95 (+1.30%)

Contract

0xAa77Ffa2aec0F06786E7ecA573CB1Cf89A7e7692
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60806040205353322024-08-15 16:49:47578 days ago1723740587  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ThrowAwayFacet

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 100000 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {Modifiers} from "contracts/facets/migration-redeem-vault/ThrowAwayModifiers.sol";
import {VAULT} from "contracts/libraries/Constants.sol";
import {console} from "contracts/libraries/console.sol";

// TODO: Add facet, call, than remove
contract ThrowAwayFacet is Modifiers {
    /////// For Migration Testing - Deprecated ///////
    function getBaseOracle() external view returns (address) {
        return s.baseOracle;
    }

    function getFlaggerIdCounter() external view returns (uint24) {
        return s.flaggerIdCounter;
    }

    function getTokenIdCounter() external view returns (uint40) {
        return s.tokenIdCounter;
    }

    function getReentrantStatus() external view returns (uint8) {
        return s.reentrantStatus;
    }

    function getDethVault(address deth) external view returns (uint256) {
        return s.dethVault[deth];
    }

    function getFlagMapping(uint24 flaggerId) external view returns (address) {
        return s.flagMapping[flaggerId];
    }

    function getFiller1() external view returns (uint256) {
        return s.filler1;
    }

    function getFiller2() external view returns (uint256) {
        return s.filler2;
    }

    function getFiller3() external view returns (uint256) {
        return s.filler3;
    }

    function getNFTName() external view returns (string memory) {
        return s.name;
    }

    function getNFTSymbol() external view returns (string memory) {
        return s.symbol;
    }

    function v2StorageMigration(address yDUSD) external onlyAdminOrDAO {
        address dusd = address(0xD177000a2BC4F4d2246F0527Ad74Fd4140e029fd);
        address deth = address(0xd1770004661852cbC0B317c7775f4fA22E6bC60A);
        address _diamond = address(this);

        // run once
        require(s.yieldVault[dusd] != yDUSD, "already run");

        // assertEq(s.asset[dusd].callerFeePct, 5);
        // assertEq(s.asset[dusd].recoveryCR, 140);
        // assertEq(s.yieldVault[dusd], address(0));
        // assertNotEq(s.asset[dusd].lastRedemptionTime, 0);
        // asertEq(s.shortRecords[dusd][address(this)][2].updatedAt, 495516439307985975508992);
        // assertEq(s.shortRecords[dusd][address(this)][2].ercDebtRate, 586);

        // proposal 1
        // set DUSD callerFeePct to 5% from 0.5%
        s.asset[dusd].callerFeePct = 50;
        // proposal 2
        // set DUSD recoveryCR to 150% from 140%
        s.asset[dusd].recoveryCR = 150;

        // new vault feature
        // set DUSD vault to yDUSD from 0
        s.yieldVault[dusd] = yDUSD;

        // storage migration 1
        // set DUSD lastRedemptionTime to 0 from non-zero
        s.asset[dusd].lastRedemptionTime = 0;

        // storage migration 2
        // reset TAPP SR updatedAt to 38430958 from 0
        s.shortRecords[dusd][_diamond][2].updatedAt = 38430958;
        // reset ercDebtRate from to 0
        delete s.shortRecords[dusd][_diamond][2].ercDebtRate;

        // storage migration 3
        // consolidate filler
        delete s.assetUser[dusd][_diamond].filler1;
        s.assetUser[dusd][_diamond].shortRecordCounter = 3;

        // storage migration 4
        // remove old bridges
        address bridgeReth = s.vaultBridges[VAULT.ONE][0];
        address bridgeSteth = s.vaultBridges[VAULT.ONE][1];
        delete s.bridge[bridgeReth];
        delete s.bridge[bridgeSteth];
        s.vaultBridges[1].pop();
        s.vaultBridges[1].pop();

        // reset these to 0
        delete s.flaggerIdCounter;
        delete s.tokenIdCounter;
        delete s.reentrantStatus;
        delete s.dethVault[deth];
        delete s.name;
        delete s.symbol;
    }
}

File 2 of 53 : ThrowAwayModifiers.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {LibDiamond} from "contracts/libraries/LibDiamond.sol";
import {Errors} from "contracts/libraries/Errors.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
}

// 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 + 32 = 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 + 8 + 16 + 8 = 192 (64 unused)
        address addr; // 160
        O prevOrderType;
        // @dev storing as 170 with 2 decimals -> 1.70 ether
        uint16 shortOrderCR; // @dev CR from the shorter only used for limit short
        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: 88 + 80 + 32 + 8 + 8 + 8 + 8 = 216 (24 remaining)
        SR status;
        uint8 prevId;
        uint8 id;
        uint8 nextId;
        uint80 ercDebtRate; // socialized penalty rate
        uint32 updatedAt; // seconds
        uint88 ercDebtFee;
        uint24 filler1;
    }

    // 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 + 8*8 + 16*2 + 32 = 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 penaltyCR; // 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 baseRate;
        uint16 liquidationCR; // 1.5 ether -> [1-5, 2 decimals]
        uint8 recoveryCR; // 1.5 ether -> [1-2, 2 decimals]
        // TODO: Make fn and set lastRedemption to ZERO on mainnet remove fn
        uint32 lastRedemptionTime; // in seconds;
        uint64 filler1; // ercDebtRate used to be here
        // SLOT 3 (Chainlink)
        // 160 (96 unused)
        address oracle; // for non-usd asset
        uint96 filler2;
        // SLOT 4 (Discount)
        // 104 + 32 + 32 + 16 + 16 = 200 (56 unused)
        uint104 discountedErcMatched;
        uint32 initialDiscountTime;
        uint32 lastDiscountTime;
        uint16 discountPenaltyFee;
        uint16 discountMultiplier;
        uint56 filler3;
        // SLOT 5 (debtFee)
        // 88 + 80 = 168 (88 unused)
        uint88 ercDebtFee;
        // TODO: ercDebtRate moved from slot 2 to slot 5. Account for this in migration
        uint80 ercDebtRate; // socialized penalty rate
        uint88 filler4;
    }

    // 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 = 120 (136 unused)
        // tracked for shorter ditto rewards
        uint88 dethCollateralReward; // onlyUp
        uint16 dethTithePercent; // [0-100, 2 decimals]
        uint16 dittoShorterRate; // per unit of dethCollateral
        uint136 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
    }

    struct AssetUser {
        // SLOT 1: 104 + 8 = 112 (144 unused)
        uint104 ercEscrowed;
        uint8 shortRecordCounter;
        uint144 filler1;
        // SLOT 2: 160 + 8 = 168 (88 unused)
        address SSTORE2Pointer;
        uint8 slateLength;
        uint88 filler2;
    }

    // struct AssetUser {
    //     // SLOT 1: 104 + 56 + 8 = 168 (88 unused)
    //     uint104 ercEscrowed;
    //     uint56 filler1;
    //     uint8 shortRecordCounter;
    //     uint88 filler2;
    //     // SLOT 2: 160 + 8 = 168 (88 unused)
    //     address SSTORE2Pointer;
    //     uint8 slateLength;
    //     uint88 filler3;
    // }

    // 1 slots
    struct VaultUser {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 ethEscrowed;
        uint88 dittoMatchedShares;
        uint80 dittoReward; // max 1.2m, 18 decimals
        // SLOT 2: 88 + 88 = 172 (80 unused)
        // Credits only needed for VAULT.ONE with mixed LST
        uint88 bridgeCreditReth;
        uint88 bridgeCreditSteth;
    }

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

struct AppStorage {
    address admin;
    address ownerCandidate;
    address baseOracle;
    uint24 flaggerIdCounter; // UNUSED: flaggerIdCounter deprecated
    uint40 tokenIdCounter; // UNUSED: tokenIdCounter deprecated
    uint8 reentrantStatus; // UNUSED: reentrantStatus deprecated (1)
    mapping(address deth => uint256 vault) dethVault; // UNUSED: depositDeth/withdrawDeth removed
    // 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; // UNUSED: flagMapping deprecated
    uint256 filler1;
    uint256 filler2;
    uint256 filler3;
    address[] assets; // UNUSED: assets deprecated
    // ERC4626
    mapping(address asset => address vault) yieldVault; // Using the slot previous allocated for filler4
    // ERC721 - METADATA STORAGE/LOGIC
    string name;
    string symbol;
}

contract Modifiers {
    AppStorage internal s;

    error NotOwnerOrAdmin();

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

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

File 3 of 53 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

library C {
    // @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 BID_CR = 1 ether;

    // Redemptions
    uint256 internal constant ONE_CR = 1 ether;
    uint256 internal constant DISPUTE_REDEMPTION_BUFFER = 3600 seconds; // 1 hour
    uint256 internal constant BETA = 2 ether;
    /*
     * Half-life of 12h. 12h = 43200 seconds
     * (1/2) = d^43200 => d = (1/2)^(1/43200)
     */
    uint256 public constant SECONDS_DECAY_FACTOR = 0.9999839550551 ether;

    uint256 internal constant ROUNDING_ZERO = 100 wei; // @dev Using 100 wei as approximation for 0 to account for rounding
    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 CRATIO_MAX_INITIAL = CRATIO_MAX - 1 ether; // @dev minus 1 bc it comes from bidder
    uint256 internal constant YIELD_DELAY_SECONDS = 60; // just need enough to prevent flash loan
    uint256 internal constant UPDATE_THRESHOLD = 1000 ether;
    uint256 internal constant BRIDGE_YIELD_PERCENT_THRESHOLD = 0.01 ether; // 1%

    // Tithe
    uint16 internal constant MAX_TITHE = 100_00;
    uint16 internal constant INITIAL_TITHE_MOD = 0;

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

    // reentrancy
    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 ONE_THIRD = 0.333333333333333333 ether;

    // @dev changing this will likely break the end to end fork test
    uint256 internal constant STARTING_TIME = 1698710400;

    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 ERC contract sets to 6 decimals

    // Price Peg
    uint256 internal constant DISCOUNT_UPDATE_THRESHOLD = 100000 ether;
    uint64 internal constant DISCOUNT_THRESHOLD = 0.01 ether;
    uint32 internal constant MAX_DAYS_ELAPSED = 14;
}

library VAULT {
    // ONE is the default vault
    uint256 internal constant ONE = 1;
    // Bridges for Vault ONE
    uint256 internal constant BRIDGE_RETH = 0;
    uint256 internal constant BRIDGE_STETH = 1;
    // TWAP for Vault ONE
    address internal constant WSTETH_WETH = address(0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa);
    address internal constant WSTETH = address(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
    // @dev MUST redeploy if rETH address changes in Rocket Storage
    address internal constant RETH_WETH = address(0x553e9C493678d8606d6a5ba284643dB2110Df823);
    address internal constant RETH = address(0xae78736Cd615f374D3085123A210448E74Fc6393);
}

File 4 of 53 : console.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {U256} from "contracts/libraries/PRBMathHelper.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

import {IDiamondLoupe} from "contracts/interfaces/IDiamondLoupe.sol";
import {STypes, MTypes, O, SR} from "contracts/libraries/DataTypes.sol";
import {C} from "contracts/libraries/Constants.sol";
import {TestTypes} from "test/utils/TestTypes.sol";

address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

/* solhint-disable */
function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn)
    pure
    returns (function(bytes memory) internal pure fnOut)
{
    assembly {
        fnOut := fnIn
    }
}

function _sendLogPayload(bytes memory payload) pure {
    _castLogPayloadViewToPure(_sendLogPayloadView)(payload);
}

function _sendLogPayloadView(bytes memory payload) view {
    uint256 payloadLength = payload.length;
    address consoleAddress = CONSOLE_ADDRESS;
    assembly {
        let payloadStart := add(payload, 32)
        let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
    }
}
/* solhint-enable */

// solhint-disable-next-line contract-name-camelcase
library console {
    using U256 for uint256;

    function logBytes(bytes memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes4(bytes4 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes8(bytes8 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes11(bytes11 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function log(uint256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function log(uint256 p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
    }

    function log(uint256 p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
    }

    function log(int256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(int)", p0));
    }

    function log(string memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(string memory p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(O o) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", o));
    }

    function log(SR status) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", status));
    }

    // @dev may not be in sync with DataTypes
    function orderTypetoString(O o) private pure returns (string memory orderType) {
        string[] memory typeToString = new string[](8);
        typeToString[0] = "Uninitialized";
        typeToString[1] = "LimitBid";
        typeToString[2] = "LimitAsk";
        typeToString[3] = "MarketBid";
        typeToString[4] = "MarketAsk";
        typeToString[5] = "LimitShort";
        typeToString[6] = "Cancelled";
        typeToString[7] = "Matched";
        return typeToString[uint8(o)];
    }

    function orderTypetoString2(O o) private pure returns (string memory orderType) {
        string[] memory typeToString = new string[](8);
        typeToString[0] = "U!";
        typeToString[1] = "LB";
        typeToString[2] = "LA";
        typeToString[3] = "MA";
        typeToString[4] = "MB";
        typeToString[5] = "LS";
        typeToString[6] = "C!";
        typeToString[7] = "M!";
        return typeToString[uint8(o)];
    }

    function padId(uint16 id) private pure returns (string memory _id) {
        if (id == 1) {
            return "HED";
        }

        return Strings.toString(id);
    }

    function shortRecordStatustoString(SR s) private pure returns (string memory orderType) {
        string[] memory typeToString = new string[](3);
        typeToString[0] = "PartialFill";
        typeToString[1] = "FullyFilled";
        typeToString[2] = "Cancelled";
        return typeToString[uint8(s)];
    }

    function addrToString(address a) private pure returns (string memory label) {
        // 0x0000000000000000000000000000000000000002 -> 2
        string[] memory typeToString = new string[](4);
        typeToString[0] = "zero(0)";
        typeToString[1] = "receiver(1)";
        typeToString[2] = "sender(2)";
        typeToString[3] = "extra(3)";

        uint160 num = uint160(a);

        if (num <= 3) {
            return typeToString[num];
        }
        return "addr";
    }

    function newLine() internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", ""));
    }

    function logId(STypes.Order memory _order) internal pure {
        string memory orderType = orderTypetoString2(_order.orderType);
        _sendLogPayload(
            abi.encodeWithSignature(
                "log(string,string,string,string)",
                _order.id == C.HEAD ? "H!" : orderType,
                padId(_order.prevId),
                padId(_order.id),
                padId(_order.nextId)
            )
        );
    }

    function log(IDiamondLoupe.Facet memory _facet) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", "Facet"));
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", "facetAddress:", _facet.facetAddress));
        for (uint256 i = 0; i < _facet.functionSelectors.length; i++) {
            logBytes4(_facet.functionSelectors[i]);
        }
        newLine();
    }

    function log(IDiamondLoupe.Facet[] memory _facets) internal pure {
        for (uint256 i = 0; i < _facets.length; i++) {
            log(_facets[i]);
        }
    }

    function log(STypes.Order memory _order) internal pure {
        if (_order.id == 1) {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", "HEAD:", _order.prevId, _order.id, _order.nextId));
        } else {
            _sendLogPayload(
                abi.encodeWithSignature(
                    "log(string)",
                    string.concat(
                        orderTypetoString(_order.orderType),
                        // Strings.toString(uint8(_order.orderType)),
                        ": ",
                        addrToString(_order.addr),
                        ", cTime: ",
                        Strings.toString(_order.creationTime),
                        ", iCR: ",
                        Strings.toString(_order.shortOrderCR)
                    )
                )
            );
            _sendLogPayload(
                abi.encodeWithSignature("log(string,uint,uint,uint)", "id(s):", _order.prevId, _order.id, _order.nextId)
            );
            _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "price:", _order.price));
            _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "ercAmount:", _order.ercAmount));
            _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "shortRecordId:", _order.shortRecordId));
        }

        newLine();
    }

    function log(STypes.Order[] memory _orders) internal pure {
        for (uint256 i = 0; i < _orders.length; i++) {
            log(_orders[i]);
        }
    }

    function log(STypes.ShortRecord memory _short) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", "Short"));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", "id(s):", _short.prevId, _short.nextId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "updatedAt:", _short.updatedAt));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "dethYieldRate:", _short.dethYieldRate));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "collateral:", _short.collateral));
        _sendLogPayload(abi.encodeWithSignature("log(string)", shortRecordStatustoString(_short.status)));

        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "ercDebt:", _short.ercDebt));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "ercDebtFee:", _short.ercDebtFee));
    }

    function zeroPad(uint256 length, uint256 number) internal pure returns (string memory) {
        string memory numberStr = Strings.toString(number);

        uint256 zeros = length - bytes(numberStr).length;
        bytes memory buffer = new bytes(length);
        uint256 index = 0;
        while (index < zeros) {
            buffer[index++] = "0";
        }
        for (uint256 i = 0; i < bytes(numberStr).length; i++) {
            buffer[index++] = bytes(numberStr)[i];
        }
        return string(buffer);
    }

    function weiToEther(uint256 amountInWei) public pure returns (string memory) {
        // Convert wei to ether (1 ether = 10^18 wei)
        uint256 amountInEther = amountInWei / 1 ether;
        // Calculate the fractional part (wei remaining after converting to ether)
        uint256 fractionalWei = amountInWei % 1 ether;
        string memory fractionalString = zeroPad(18, fractionalWei);
        return string.concat(Strings.toString(amountInEther), ".", fractionalString);
    }

    function logErcDebt(address user, uint256 ercDebt) internal pure {
        string memory ercDebtInEther = weiToEther(ercDebt);

        if (uint256(uint160(user)) < 9) {
            _sendLogPayload(
                abi.encodeWithSignature("log(string,string)", string.concat("0x", Strings.toString(uint160(user))), ercDebtInEther)
            );
        } else {
            _sendLogPayload(abi.encodeWithSignature("log(address,string)", user, ercDebtInEther));
        }
    }

    function log(STypes.ShortRecord[] memory _srs) internal pure {
        for (uint256 i = 0; i < _srs.length; i++) {
            log(_srs[i]);
        }
    }

    function log(TestTypes.StorageUser memory _storageUser) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", "Storage User"));
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", "addr:", _storageUser.addr));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "ethEscrowed:", _storageUser.ethEscrowed));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "ercEscrowed:", _storageUser.ercEscrowed));
    }

    function log(MTypes.BidMatchAlgo memory _b) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "askId", _b.askId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "shortHintId", _b.shortHintId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "shortId", _b.shortId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "prevShortId", _b.prevShortId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "firstShortIdBelowOracle", _b.firstShortIdBelowOracle));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "matchedAskId", _b.matchedAskId));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "matchedShortId", _b.matchedShortId));
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", "isMovingBack", _b.isMovingBack));
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", "isMovingFwd", _b.isMovingFwd));
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", "oraclePrice", _b.oraclePrice));
    }

    /* solhint-disable no-console */
    function logBids(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.bids[asset][C.HEAD];
        console.log(o);

        uint16 currentId = o.nextId;
        while (currentId != C.TAIL) {
            o = s.bids[asset][currentId];
            console.log(o);
            currentId = o.nextId;
        }
        console.log("--");
    }

    function logAsks(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.asks[asset][C.HEAD];
        console.log(o);
        uint16 currentId = o.nextId;
        while (currentId != C.TAIL) {
            o = s.asks[asset][currentId];
            console.log(o);
            currentId = o.nextId;
        }
        console.log("--");
    }

    function logShorts(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.shorts[asset][C.HEAD];
        console.log(o);
        uint16 currentId = o.nextId;
        while (currentId != C.TAIL) {
            o = s.shorts[asset][currentId];
            console.log(o);
            currentId = o.nextId;
        }
        console.log("--");
    }

    function logInactiveBids(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.bids[asset][C.HEAD];
        console.log(o);
        uint16 currentId = o.prevId;
        while (currentId != C.HEAD) {
            o = s.bids[asset][currentId];
            console.log(o);
            currentId = o.prevId;
        }
        console.log("--");
    }

    function logInactiveAsks(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.asks[asset][C.HEAD];
        console.log(o);
        uint16 currentId = o.prevId;
        while (currentId != C.HEAD) {
            o = s.asks[asset][currentId];
            console.log(o);
            currentId = o.prevId;
        }
        console.log("--");
    }

    function logInactiveShorts(address asset) external view {
        AppStorage storage s = appStorage();
        STypes.Order memory o = s.shorts[asset][C.HEAD];
        console.log(o);
        uint16 currentId = o.prevId;
        while (currentId != C.HEAD) {
            o = s.shorts[asset][currentId];
            console.log(o);
            currentId = o.prevId;
        }
        console.log("--");
    }

    function logAllShorts(address asset) internal view {
        AppStorage storage s = appStorage();
        uint16 currentId = s.shorts[asset][C.HEAD].prevId;
        uint256 prevOrderSize;
        uint256 nextOrderSize;
        uint16 lastPrevId = C.HEAD;
        while (currentId != C.HEAD) {
            lastPrevId = currentId;
            currentId = s.shorts[asset][currentId].prevId;
            prevOrderSize++;
        }
        currentId = s.shorts[asset][C.HEAD].nextId;
        while (currentId != C.TAIL) {
            nextOrderSize++;
            currentId = s.shorts[asset][currentId].nextId;
        }

        STypes.Order[] memory orderArr = new STypes.Order[](prevOrderSize + nextOrderSize + 1);
        orderArr[prevOrderSize] = s.shorts[asset][C.HEAD];

        currentId = s.shorts[asset][C.HEAD].prevId;
        for (uint256 i = 0; i < prevOrderSize; i++) {
            orderArr[prevOrderSize - i - 1] = s.shorts[asset][currentId];
            currentId = s.shorts[asset][currentId].prevId;
        }
        currentId = s.shorts[asset][C.HEAD].nextId;
        for (uint256 i = 0; i < nextOrderSize; i++) {
            orderArr[prevOrderSize + i + 1] = s.shorts[asset][currentId];
            currentId = s.shorts[asset][currentId].nextId;
        }

        console.log("==LOG SHORTS==");
        for (uint256 i = 0; i < orderArr.length; i++) {
            console.logId(orderArr[i]);
        }
        console.log("== ==");
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/**
 * \
 * Author: Nick Mudge
 *
 * Implementation of Diamond facet.
 * Uses the diamond-2 version 1.3.4 implementation:
 * https://github.com/mudgen/diamond-2-hardhat/blob/c455afbe2487f3878581e8edd69721ac17d6e973/contracts/libraries/LibDiamond.sol
 *
 * 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) {
            enforceHasContractCode(_newFacetAddress, "LibDiamondCut: Add facet has no code");
            for (uint256 selectorIndex; selectorIndex < _selectors.length;) {
                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);
                // "_selectorCount & 7" is a gas efficient modulo by eight "_selectorCount % 8"
                // " << 5 is the same as multiplying by 32 ( * 32)
                uint256 selectorInSlotPosition = (_selectorCount & 7) << 5;
                // 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) {
                    // "_selectorSlot >> 3" is a gas efficient division by 8 "_selectorSlot / 8"
                    ds.selectorSlots[_selectorCount >> 3] = _selectorSlot;
                    _selectorSlot = 0;
                }
                _selectorCount++;

                unchecked {
                    selectorIndex++;
                }
            }
        } else if (_action == IDiamondCut.FacetCutAction.Replace) {
            enforceHasContractCode(_newFacetAddress, "LibDiamondCut: Replace facet has no code");
            for (uint256 selectorIndex; selectorIndex < _selectors.length;) {
                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);

                unchecked {
                    selectorIndex++;
                }
            }
        } else if (_action == IDiamondCut.FacetCutAction.Remove) {
            require(_newFacetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
            // "_selectorCount >> 3" is a gas efficient division by 8 "_selectorCount / 8"
            uint256 selectorSlotCount = _selectorCount >> 3;
            // "_selectorCount & 7" is a gas efficient modulo by eight "_selectorCount % 8"
            uint256 selectorInSlotIndex = _selectorCount & 7;
            for (uint256 selectorIndex; selectorIndex < _selectors.length;) {
                if (_selectorSlot == 0) {
                    // get last selectorSlot
                    selectorSlotCount--;
                    _selectorSlot = ds.selectorSlots[selectorSlotCount];
                    selectorInSlotIndex = 7;
                } else {
                    selectorInSlotIndex--;
                }
                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
                    // " << 5 is the same as multiplying by 32 ( * 32)
                    lastSelector = bytes4(_selectorSlot << (selectorInSlotIndex << 5));
                    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));
                    // "oldSelectorCount >> 3" is a gas efficient division by 8 "oldSelectorCount / 8"
                    oldSelectorsSlotCount = oldSelectorCount >> 3;
                    // "oldSelectorCount & 7" is a gas efficient modulo by eight "oldSelectorCount % 8"
                    // " << 5 is the same as multiplying by 32 ( * 32)
                    oldSelectorInSlotPosition = (oldSelectorCount & 7) << 5;
                }
                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;
                }

                unchecked {
                    selectorIndex++;
                }
            }
            _selectorCount = selectorSlotCount * 8 + selectorInSlotIndex;
        } 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 6 of 53 : Errors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

library Errors {
    error AlreadyLiquidatable();
    error AlreadyMinted();
    error AssetIsFrozen();
    error AssetIsNotPermanentlyFrozen();
    error BadHintIdArray();
    error BadShortHint();
    error BelowRecoveryModeCR();
    error BridgeAlreadyCreated();
    error CannotCancelMoreThan1000Orders();
    error CannotDisputeWithRedeemerProposal();
    error CannotDisputeYourself();
    error CannotExitPartialFillSR();
    error CannotLeaveDustAmount();
    error CannotLiquidateSelf();
    error CannotMakeMoreThanMaxSR();
    error CannotRemoveEthInitial();
    error CannotSocializeDebt();
    error CannotTransferSRToTapp();
    error CanOnlyClaimYourShort();
    error CollateralHigherThanMax();
    error CombinedShortBelowCRThreshold();
    error CRLowerThanMin();
    error DifferentVaults();
    error DisputeSRUpdatedNearProposalTime();
    error ERC4626CannotMint();
    error ERC4626CannotRedeem();
    error ERC4626CannotWithdrawBeforeDiscountWindowExpires();
    error ERC4626DepositMoreThanMax();
    error ERC4626DepositSlippageExceeded();
    error ERC4626ExistingWithdrawalProposal();
    error ERC4626InvalidSlippage();
    error ERC4626InvalidOwner();
    error ERC4626MaxWithdrawTimeHasElapsed();
    error ERC4626ProposeWithdrawFirst();
    error ERC4626WaitLongerBeforeWithdrawing();
    error ERC4626WithdrawMoreThanMax();
    error ERC4626WithdrawSlippageExceeded();
    error ERC4626AmountProposedTooLow();
    error ERC4626SyncError();
    error ExistingProposedRedemptions();
    error ExitShortPriceTooLow();
    error FirstShortDeleted();
    error FunctionNotFound(bytes4 _functionSelector);
    error InsufficientWalletBalance();
    error InsufficientCollateral();
    error InsufficientERCEscrowed();
    error InsufficientETHEscrowed();
    error InsufficientEthInLiquidityPool();
    error InsufficientNumberOfShorts();
    error InvalidAmount();
    error InvalidAsset();
    error InvalidBridge();
    error InvalidBuyback();
    error InvalidCR();
    error InvalidMsgValue();
    error InvalidNumberOfShortOrderIds();
    error InvalidPrice();
    error InvalidRedemption();
    error InvalidRedemptionDispute();
    error InvalidShortId();
    error InvalidShortOrder();
    error InvalidTithe();
    error InvalidTwapPrice();
    error InvalidTWAPSecondsAgo();
    error InvalidVault();
    error InvalidDeth();
    error LiquidationIneligibleWindow();
    error SecondaryLiquidationNoValidShorts();
    error MarketAlreadyCreated();
    error MustUseExistingBridgeCredit();
    error NoDittoReward();
    error NoSells();
    error NoShares();
    error NotActiveOrder();
    error NotBridgeForBaseCollateral();
    error NotDiamond();
    error NotLastOrder();
    error NotLowestIncorrectIndex();
    error NotMinted();
    error NotOwner();
    error NotOwnerOrAdmin();
    error NotOwnerCandidate();
    error NoValidShortRecordsForRedemption();
    error NoYield();
    error OrderIdCountTooLow();
    error OrderUnderMinimumSize();
    error OriginalShortRecordCancelled();
    error ParameterIsZero();
    error PostExitCRLtPreExitCR();
    error PriceOrAmountIs0();
    error ProposalExpired(uint256);
    error ProposalInputsNotSorted();
    error ReceiverExceededShortRecordLimit();
    error RedemptionFeeTooHigh();
    error RedemptionUnderMinShortErc();
    error ReentrantCall();
    error ReentrantCallView();
    error ShortRecordAlreadyRedeemed();
    error ShortRecordFullyFilledUnderMinSize();
    error ShortRecordIdOverflow();
    error ShortRecordIdsNotSorted();
    error ShortRecordLacksMinCollateral();
    error SufficientCollateral();
    error TimeToDisputeHasElapsed();
    error TimeToDisputeHasNotElapsed();
    error TooManyHints();
    error TooManyProposals();
    error UnderMinimum();
    error UnderMinimumDeposit();
    error VaultAlreadyCreated();
}

File 7 of 53 : PRBMathHelper.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {mulDiv as _mulDiv, mulDiv18, UNIT} from "@prb/Common.sol";
import {wrap as _wrap, unwrap as _unwrap} from "@prb/ud60x18/Casting.sol";
import {UD60x18} from "@prb/ud60x18/ValueType.sol";
import {pow as _pow, powu as _powu} from "@prb/ud60x18/Math.sol";
import {Errors} from "contracts/libraries/Errors.sol";
// import {console} from "contracts/libraries/console.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);
    }

    function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        UD60x18 _x = _wrap(x);
        UD60x18 _y = _wrap(y);
        result = _unwrap(_pow(_x, _y));
    }

    function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        UD60x18 _x = _wrap(x);
        result = _unwrap(_powu(_x, y));
    }
}

// uint128
library U128 {
    // 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 U104 {
    // 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 mulU104(uint104 x, uint256 y) internal pure returns (uint104 result) {
        uint256 _result = mulDiv18(x, y);
        if (_result > type(uint104).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint104(_result);
    }

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

    function divU104(uint104 x, uint256 y) internal pure returns (uint104 result) {
        uint256 _result = _mulDiv(x, UNIT, y);
        if (_result > type(uint104).max) revert Errors.InvalidAmount(); // assume amount?
        result = uint104(_result);
    }
}
// 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;
        }
    }
}

// uint64

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

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

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

    function divU64(uint64 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);
    }
}

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

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

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

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

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

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

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

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

    function divU80(uint16 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);
    }
}

File 8 of 53 : AppStorage.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {STypes, F} from "contracts/libraries/DataTypes.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";
import {LibSRUtil} from "contracts/libraries/LibSRUtil.sol";
import {tstore, tload} from "contracts/libraries/LibTStore.sol";
import {Errors} from "contracts/libraries/Errors.sol";

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

struct AppStorage {
    address admin;
    address ownerCandidate;
    address baseOracle;
    uint24 flaggerIdCounter; // UNUSED: flaggerIdCounter deprecated
    uint40 tokenIdCounter; // UNUSED: tokenIdCounter deprecated
    uint8 reentrantStatus; // UNUSED: reentrantStatus deprecated (1)
    mapping(address deth => uint256 vault) dethVault; // UNUSED: depositDeth/withdrawDeth removed
    // 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; // UNUSED: flagMapping deprecated
    uint256 filler1;
    uint256 filler2;
    uint256 filler3;
    address[] assets; // UNUSED: assets deprecated
    // ERC4626
    mapping(address asset => address vault) yieldVault; // Using the slot previous allocated for filler4
    // ERC721 - METADATA STORAGE/LOGIC
    string name; // UNUSED: name deprecated
    string symbol; // UNUSED: symbol deprecated
}

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();
        _;
    }

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

    modifier nonReentrant() {
        if (tload(0) == 1) revert Errors.ReentrantCall();

        tstore(0, 1);
        _;
        tstore(0, 0);
    }

    modifier nonReentrantView() {
        if (tload(0) == 1) revert Errors.ReentrantCallView();
        _;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/*
 * Author: Nick Mudge <nick@perfectabstractions.com> (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 11 of 53 : DataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

// 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 + 32 = 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 + 8 + 16 + 8 = 192 (64 unused)
        address addr; // 160
        O prevOrderType;
        // @dev storing as 170 with 2 decimals -> 1.70 ether
        uint16 shortOrderCR; // @dev CR from the shorter only used for limit short
        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: 88 + 80 + 32 + 8 + 8 + 8 + 8 = 216 (24 remaining)
        SR status;
        uint8 prevId;
        uint8 id;
        uint8 nextId;
        uint80 ercDebtRate; // socialized penalty rate
        uint32 updatedAt; // seconds
        uint88 ercDebtFee;
        uint24 filler1;
    }

    // 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 + 8*8 + 16*2 + 32 = 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 penaltyCR; // 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 baseRate;
        uint16 liquidationCR; // 1.5 ether -> [1-5, 2 decimals]
        uint8 recoveryCR; // 1.5 ether -> [1-2, 2 decimals]
        // TODO: Make fn and set lastRedemption to ZERO on mainnet remove fn
        uint32 lastRedemptionTime; // in seconds;
        uint8 redemptionCR; // 2.0 ether -> [1-2, 2 decimals]
        uint56 filler1; // ercDebtRate used to be here (as uint64)
        // SLOT 3 (Chainlink)
        // 160 (96 unused)
        address oracle; // for non-usd asset
        uint96 filler2;
        // SLOT 4 (Discount)
        // 104 + 32 + 32 + 16 + 16 = 200 (56 unused)
        uint104 discountedErcMatched;
        uint32 initialDiscountTime;
        uint32 lastDiscountTime;
        uint16 discountPenaltyFee;
        uint16 discountMultiplier;
        uint56 filler3;
        // SLOT 5 (debtFee)
        // 88 + 80 = 168 (88 unused)
        uint88 ercDebtFee;
        // TODO: ercDebtRate moved from slot 2 to slot 5. Account for this in migration
        uint80 ercDebtRate; // socialized penalty rate
        uint88 filler4;
    }

    // 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 = 120 (136 unused)
        // tracked for shorter ditto rewards
        uint88 dethCollateralReward; // onlyUp
        uint16 dethTithePercent; // [0-100, 2 decimals]
        uint16 dittoShorterRate; // per unit of dethCollateral
        uint136 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
    }

    struct AssetUser {
        // SLOT 1: 104 + 8 = 112 (144 unused)
        uint104 ercEscrowed;
        uint8 shortRecordCounter;
        uint144 filler1;
        // SLOT 2: 160 + 8 = 168 (88 unused)
        address SSTORE2Pointer;
        uint8 slateLength;
        uint88 filler2;
    }

    // 1 slots
    struct VaultUser {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 ethEscrowed;
        uint88 dittoMatchedShares;
        uint80 dittoReward; // max 1.2m, 18 decimals
        // SLOT 2: 88 + 88 = 172 (80 unused)
        // Credits only needed for VAULT.ONE with mixed LST
        uint88 bridgeCreditReth;
        uint88 bridgeCreditSteth;
    }

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

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

    struct BatchLiquidation {
        address shorter;
        uint8 shortId;
        uint16 shortOrderId;
    }

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

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

    struct CombineShorts {
        uint88 collateral;
        uint88 ercDebt;
        uint256 yield;
        uint256 ercDebtSocialized;
        uint88 ercDebtFee;
    }

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

    struct SecondaryLiquidation {
        STypes.ShortRecord short;
        uint16 shortOrderId;
        bool isPartialFill;
        address shorter;
        uint88 liquidatorCollateral;
        uint256 cRatio;
    }

    struct AssetParams {
        address asset;
        uint256 penaltyCR;
        uint256 oraclePrice;
        uint256 liquidationCR;
        uint256 minShortErc;
        uint80 ercDebtRate;
    }

    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 {
        uint256 eth;
        uint256 CR;
        uint256 initialCR;
        uint256 ethInitial;
        uint256 minShortErc;
        uint256 minAskEth;
        uint256 oraclePrice;
    }

    // @dev saved via SSTORE2
    // @dev total bytes: 232 + 176 = 408
    struct ProposalData {
        // SLOT 1: 160 + 8 + 88 = 232 (24 unused)
        address shorter;
        uint8 shortId;
        uint64 CR;
        // SLOT 2: 88 + 88 = 176 (80 unused)
        uint88 ercDebtRedeemed;
        uint88 colRedeemed;
        // SLOT 3: 88
        uint88 ercDebtFee;
    }

    struct ProposalInput {
        address shorter;
        uint8 shortId;
        uint16 shortOrderId;
    }

    struct ProposeRedemption {
        address asset;
        address shorter;
        uint8 shortId;
        uint16 shortOrderId;
        uint32 protocolTime;
        uint88 totalAmountProposed;
        uint88 totalColRedeemed;
        uint256 currentCR;
        uint256 lastCR;
        uint8 redemptionCounter;
        uint80 oraclePrice;
        uint88 amountProposed;
        uint88 colRedeemed;
        uint80 ercDebtRate;
        uint88 ercDebtFee;
        uint88 totalErcDebtFee;
    }

    struct DisputeRedemption {
        address asset;
        address redeemer;
        uint32 timeProposed;
        uint32 timeToDispute;
        uint80 oraclePrice;
        uint80 ercDebtRate;
        MTypes.ProposalData[] decodedProposalData;
        uint88 incorrectCollateral;
        uint88 incorrectErcDebt;
        uint256 minShortErc;
        uint256 disputeCR;
        uint32 protocolTime;
        uint80 dethYieldRate;
        uint88 ercDebtFee;
    }

    struct HandleDiscount {
        address asset;
        uint256 savedPrice;
        uint256 ercDebt;
        uint256 price;
        uint256 ercAmount;
    }
}

File 12 of 53 : TestTypes.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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
}

enum DiscountLevels {
    Gte1,
    Gte2,
    Gte3,
    Gte4
}

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

    struct AssetNormalizedStruct {
        F frozen;
        uint16 orderId;
        uint256 initialCR;
        uint256 liquidationCR;
        uint256 forcedBidPriceBuffer;
        uint256 penaltyCR;
        uint256 tappFeePct;
        uint256 callerFeePct;
        uint16 startingShortId;
        uint256 minBidEth;
        uint256 minAskEth;
        uint256 minShortErc;
        uint256 recoveryCR;
        uint256 redemptionCR;
        uint256 discountPenaltyFee;
        uint8 assetId;
    }

    struct BridgeNormalizedStruct {
        uint256 withdrawalFee;
    }

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

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/*
 * Author: Nick Mudge <nick@perfectabstractions.com> (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 14 of 53 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions used in 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 15 of 53 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(int256(uMAX_SD1x18))) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(uint64(xUint)));
}

/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uMAX_UD2x18) {
        revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(xUint));
}

/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(uMAX_SD59x18)) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
    }
    result = SD59x18.wrap(int256(xUint));
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT128) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
    }
    result = uint128(xUint);
}

/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT40) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

File 16 of 53 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoSD59x18,
    Casting.intoUint128,
    Casting.intoUint256,
    Casting.intoUint40,
    Casting.unwrap
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.ln,
    Math.log10,
    Math.log2,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.xor
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.or as |,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.sub as -,
    Helpers.xor as ^
} for UD60x18 global;

File 17 of 53 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_UD60x18,
    uMAX_WHOLE_UD60x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    unchecked {
        result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint > uMAX_WHOLE_UD60x18) {
        revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
    }

    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `UNIT - remainder`.
        let delta := sub(uUNIT, remainder)

        // Equivalent to `x + remainder > 0 ? delta : 0`.
        result := add(x, mul(delta, gt(remainder, 0)))
    }
}

/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xUint > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        uint256 doubleUnitProduct = xUint * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
    if (xUint > uEXP2_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
    }

    // Convert x to the 192.64-bit fixed-point format.
    uint256 x_192x64 = (xUint << 64) / uUNIT;

    // Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
    result = wrap(Common.exp2(x_192x64));
}

/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `x - remainder > 0 ? remainder : 0)`.
        result := sub(x, mul(remainder, gt(remainder, 0)))
    }
}

/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        result := mod(x, uUNIT)
    }
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    if (xUint == 0 || yUint == 0) {
        return ZERO;
    }

    unchecked {
        // Checking for overflow this way is faster than letting Solidity do it.
        uint256 xyUint = xUint * yUint;
        if (xyUint / xUint != yUint) {
            revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        result = wrap(Common.sqrt(xyUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(uUNIT_SQUARED / x.unwrap());
    }
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
        // {log2} can return is ~196_205294292027477728.
        result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
    }
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
        default { result := uMAX_UD60x18 }
    }

    if (result.unwrap() == uMAX_UD60x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    unchecked {
        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(xUint / uUNIT);

        // This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
        // n is at most 255 and UNIT is 1e18.
        uint256 resultUint = n * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        uint256 y = xUint >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultUint);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        uint256 DOUBLE_UNIT = 2e18;
        for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultUint += delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        result = wrap(resultUint);
    }
}

/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}

/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xUint == 0) {
        return yUint == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xUint == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yUint == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yUint == uUNIT) {
        return x;
    }

    // If x is greater than `UNIT`, use the standard formula.
    if (xUint > uUNIT) {
        result = exp2(mul(log2(x), y));
    }
    // Conversely, if x is less than `UNIT`, use the equivalent formula.
    else {
        UD60x18 i = wrap(uUNIT_SQUARED / xUint);
        UD60x18 w = exp2(mul(log2(i), y));
        result = wrap(uUNIT_SQUARED / w.unwrap());
    }
}

/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
    // Calculate the first iteration of the loop in advance.
    uint256 xUint = x.unwrap();
    uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    for (y >>= 1; y > 0; y >>= 1) {
        xUint = Common.mulDiv18(xUint, xUint);

        // Equivalent to `y % 2 == 1`.
        if (y & 1 > 0) {
            resultUint = Common.mulDiv18(resultUint, xUint);
        }
    }
    result = wrap(resultUint);
}

/// @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:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18 / UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    unchecked {
        if (xUint > uMAX_UD60x18 / uUNIT) {
            revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
        }
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
        // In this case, the two numbers are both the square root.
        result = wrap(Common.sqrt(xUint * uUNIT));
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

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

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

// extra ShortRecord helpers, similar to LibShortRecord
library LibSRUtil {
    using U88 for uint88;
    using U256 for uint256;

    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 > C.YIELD_DELAY_SECONDS;
            if (isNotRecentlyModified) {
                s.vaultUser[vault][shorter].ethEscrowed += yield;
            } else {
                s.vaultUser[vault][address(this)].ethEscrowed += yield;
            }
        }
    }

    function invalidShortOrder(STypes.Order storage shortOrder, uint8 shortRecordId, address shorter)
        internal
        view
        returns (bool isInvalid)
    {
        if (shortOrder.shortRecordId != shortRecordId || shortOrder.addr != shorter || shortOrder.orderType != O.LimitShort) {
            return true;
        }
    }

    function checkCancelShortOrder(address asset, SR initialStatus, uint16 shortOrderId, uint8 shortRecordId, address shorter)
        internal
        returns (bool isCancelled)
    {
        AppStorage storage s = appStorage();
        if (initialStatus == SR.PartialFill) {
            STypes.Order storage shortOrder = s.shorts[asset][shortOrderId];
            if (invalidShortOrder(shortOrder, shortRecordId, shorter)) revert Errors.InvalidShortOrder();
            LibOrders.cancelShort(asset, shortOrderId);
            return true;
        }
    }

    function checkShortMinErc(address asset, SR initialStatus, uint16 shortOrderId, uint8 shortRecordId, address shorter)
        internal
    {
        AppStorage storage s = appStorage();

        STypes.ShortRecord storage shortRecord = s.shortRecords[asset][shorter][shortRecordId];
        STypes.Asset storage Asset = s.asset[asset];
        uint256 minShortErc = LibAsset.minShortErc(Asset);

        if (initialStatus == SR.PartialFill) {
            // Verify shortOrder
            STypes.Order storage shortOrder = s.shorts[asset][shortOrderId];
            if (invalidShortOrder(shortOrder, shortRecordId, shorter)) revert Errors.InvalidShortOrder();

            if (shortRecord.status == SR.Closed) {
                // Check remaining shortOrder for too little erc or too little eth
                if (shortOrder.ercAmount < minShortErc || shortOrder.shortOrderCR < Asset.initialCR) {
                    // @dev The resulting SR will not have PartialFill status after cancel
                    LibOrders.cancelShort(asset, shortOrderId);
                }
            } else {
                // Check remaining shortOrder and remaining shortRecord
                if (shortOrder.ercAmount + shortRecord.ercDebt < minShortErc) revert Errors.CannotLeaveDustAmount();
            }
        } else if (shortRecord.status != SR.Closed && shortRecord.ercDebt < minShortErc) {
            revert Errors.CannotLeaveDustAmount();
        }
    }

    function checkRecoveryModeViolation(STypes.Asset storage Asset, uint256 shortRecordCR, uint256 oraclePrice)
        internal
        view
        returns (bool recoveryViolation)
    {
        uint256 recoveryCR = LibAsset.recoveryCR(Asset);
        if (shortRecordCR < recoveryCR) {
            // Only check asset CR if low enough
            uint256 ercDebt = Asset.ercDebt;
            if (ercDebt > 0) {
                // If Asset.ercDebt == 0 then assetCR is NA
                uint256 assetCR = LibAsset.getAssetCollateralRatio(Asset, oraclePrice);
                if (assetCR < recoveryCR) {
                    // Market is in recovery mode and shortRecord CR too low
                    return true;
                }
            }
        }
    }

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

        // Distribute ercDebt
        uint80 ercDebtRate = s.asset[asset].ercDebtRate;
        updateErcDebt(short, ercDebtRate);
    }

    function updateErcDebt(STypes.ShortRecord storage short, uint80 ercDebtRate) internal {
        // Distribute ercDebt
        uint88 ercDebt = (short.ercDebt - short.ercDebtFee).mulU88(ercDebtRate - short.ercDebtRate);

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

    function onlyValidShortRecord(address asset, address shorter, uint8 id)
        internal
        view
        returns (STypes.ShortRecord storage shortRecord)
    {
        AppStorage storage s = appStorage();
        shortRecord = s.shortRecords[asset][shorter][id];
        if (shortRecord.status == SR.Closed || shortRecord.ercDebt == 0) revert Errors.InvalidShortId();
    }

    function reduceErcDebtFee(STypes.Asset storage Asset, STypes.ShortRecord storage short, uint256 ercDebtReduction) internal {
        uint88 ercDebtFeeReduction = uint88(LibOrders.min(short.ercDebtFee, ercDebtReduction)); // @dev(safe-cast)
        Asset.ercDebtFee -= ercDebtFeeReduction;
        short.ercDebtFee -= ercDebtFeeReduction;
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

function tstore(bytes32 slot, uint256 val) {
    assembly {
        tstore(slot, val)
    }
}

function tload(bytes32 slot) view returns (uint256 val) {
    assembly {
        val := tload(slot)
    }
}

library LibTStore {
    bytes32 private constant FORCEDBIDSLOT = keccak256("forcedBid");

    function setForcedBid(bool forcedBid) internal {
        tstore(FORCEDBIDSLOT, forcedBid ? 1 : 0);
    }

    function isForcedBid() internal view returns (bool) {
        uint256 val;

        bytes32 slot = FORCEDBIDSLOT;
        assembly {
            val := tload(slot)
        }

        return val == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

File 22 of 53 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);

/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);

/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);

File 23 of 53 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);

/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int64 constant uUNIT = 1e18;

File 24 of 53 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int64. This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract
/// storage.
type SD1x18 is int64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD59x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD1x18 global;

File 25 of 53 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as an SD59x18 number.
SD59x18 constant E = SD59x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
int256 constant uEXP_MAX_INPUT = 133_084258667509499440;
SD59x18 constant EXP_MAX_INPUT = SD59x18.wrap(uEXP_MAX_INPUT);

/// @dev Any value less than this returns 0 in {exp}.
int256 constant uEXP_MIN_THRESHOLD = -41_446531673892822322;
SD59x18 constant EXP_MIN_THRESHOLD = SD59x18.wrap(uEXP_MIN_THRESHOLD);

/// @dev The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);

/// @dev Any value less than this returns 0 in {exp2}.
int256 constant uEXP2_MIN_THRESHOLD = -59_794705707972522261;
SD59x18 constant EXP2_MIN_THRESHOLD = SD59x18.wrap(uEXP2_MIN_THRESHOLD);

/// @dev Half the UNIT number.
int256 constant uHALF_UNIT = 0.5e18;
SD59x18 constant HALF_UNIT = SD59x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as an SD59x18 number.
int256 constant uLOG2_10 = 3_321928094887362347;
SD59x18 constant LOG2_10 = SD59x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as an SD59x18 number.
int256 constant uLOG2_E = 1_442695040888963407;
SD59x18 constant LOG2_E = SD59x18.wrap(uLOG2_E);

/// @dev The maximum value an SD59x18 number can have.
int256 constant uMAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967;
SD59x18 constant MAX_SD59x18 = SD59x18.wrap(uMAX_SD59x18);

/// @dev The maximum whole value an SD59x18 number can have.
int256 constant uMAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MAX_WHOLE_SD59x18 = SD59x18.wrap(uMAX_WHOLE_SD59x18);

/// @dev The minimum value an SD59x18 number can have.
int256 constant uMIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
SD59x18 constant MIN_SD59x18 = SD59x18.wrap(uMIN_SD59x18);

/// @dev The minimum whole value an SD59x18 number can have.
int256 constant uMIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MIN_WHOLE_SD59x18 = SD59x18.wrap(uMIN_WHOLE_SD59x18);

/// @dev PI as an SD59x18 number.
SD59x18 constant PI = SD59x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD59x18.
int256 constant uUNIT = 1e18;
SD59x18 constant UNIT = SD59x18.wrap(1e18);

/// @dev The unit number squared.
int256 constant uUNIT_SQUARED = 1e36;
SD59x18 constant UNIT_SQUARED = SD59x18.wrap(uUNIT_SQUARED);

/// @dev Zero as an SD59x18 number.
SD59x18 constant ZERO = SD59x18.wrap(0);

File 26 of 53 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int256.
type SD59x18 is int256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoInt256,
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Math.abs,
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.log10,
    Math.log2,
    Math.ln,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.uncheckedUnary,
    Helpers.xor
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the SD59x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.or as |,
    Helpers.sub as -,
    Helpers.unary as -,
    Helpers.xor as ^
} for SD59x18 global;

File 27 of 53 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @dev Euler's number as a UD2x18 number.
UD2x18 constant E = UD2x18.wrap(2_718281828459045235);

/// @dev The maximum value a UD2x18 number can have.
uint64 constant uMAX_UD2x18 = 18_446744073709551615;
UD2x18 constant MAX_UD2x18 = UD2x18.wrap(uMAX_UD2x18);

/// @dev PI as a UD2x18 number.
UD2x18 constant PI = UD2x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD2x18.
UD2x18 constant UNIT = UD2x18.wrap(1e18);
uint64 constant uUNIT = 1e18;

File 28 of 53 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type uint64. This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract
/// storage.
type UD2x18 is uint64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoSD59x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for UD2x18 global;

File 29 of 53 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
    // This wouldn't work if x could be negative.
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 30 of 53 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);

/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);

/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);

/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);

/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);

/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);

/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);

/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);

File 31 of 53 : LibOrders.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {STypes, MTypes, O, SR} 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 {LibPriceDiscount} from "contracts/libraries/LibPriceDiscount.sol";
import {LibShortRecord} from "contracts/libraries/LibShortRecord.sol";
import {LibVault} from "contracts/libraries/LibVault.sol";
import {C} from "contracts/libraries/Constants.sol";

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

library LibOrders {
    using LibOracle for address;
    using LibVault for uint256;
    using U256 for uint256;
    using U16 for uint16;
    using U80 for uint80;
    using U88 for uint88;
    using U104 for uint104;

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

    function convertCR(uint16 cr) internal pure returns (uint256) {
        return (uint256(cr) * 1 ether) / C.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 > C.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][C.HEAD].nextId;
        uint256 size;

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

        STypes.Order[] memory list = new STypes.Order[](size);
        currentId = orders[asset][C.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();

        if (order.orderType == O.MarketBid) return;

        uint16 nextId = s.bids[asset][C.HEAD].nextId;
        uint16 hintId;
        if (order.price > s.bids[asset][nextId].price || nextId == C.TAIL) {
            hintId = C.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();

        if (order.orderType == O.MarketAsk) return;

        uint16 nextId = s.asks[asset][C.HEAD].nextId;
        uint16 hintId;
        if (order.price < s.asks[asset][nextId].price || nextId == C.TAIL) {
            hintId = C.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][C.HEAD].nextId;
        if (order.price < s.shorts[asset][nextId].price || nextId == C.TAIL) {
            hintId = C.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 eth = order.ercAmount.mul(order.price).mul(convertCR(order.shortOrderCR));
        uint256 vault = s.asset[asset].vault;
        s.vaultUser[vault][order.addr].ethEscrowed -= uint88(eth); // @dev(safe-cast)
    }

    /**
     * @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.price, incomingOrder.orderType, hintId);
        STypes.Order storage prevOrder = orders[asset][prevId];
        uint16 nextId = prevOrder.nextId;
        incomingOrder.nextId = nextId;
        incomingOrder.prevId = prevId;
        STypes.Order storage headOrder = orders[asset][C.HEAD];
        uint16 canceledID = headOrder.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 != C.HEAD) {
            STypes.Order storage cancelledOrder = orders[asset][canceledID];
            incomingOrder.prevOrderType = cancelledOrder.orderType;
            // BEFORE: CancelledID <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
            // AFTER1: CancelledID <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
            uint16 prevCanceledID = cancelledOrder.prevId;
            if (prevCanceledID != C.HEAD) {
                headOrder.prevId = prevCanceledID;
            } else {
                // BEFORE: HEAD <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
                // AFTER1: HEAD <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
                headOrder.prevId = C.HEAD;
            }
            // re-use the previous order's 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
            STypes.Asset storage Asset = s.asset[asset];
            incomingOrder.id = Asset.orderIdCounter;
            Asset.orderIdCounter += 1;
        }
        incomingOrder.creationTime = getOffsetTime();
        orders[asset][incomingOrder.id] = incomingOrder;
        if (nextId != C.TAIL) {
            orders[asset][nextId].prevId = incomingOrder.id;
        }

        prevOrder.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 == C.HEAD;
        bool check2 = _newPrice > s.bids[asset][_nextId].price || _nextId == C.TAIL;

        if (check1 && check2) {
            return C.EXACT;
        } else if (!check1) {
            return C.PREV;
        } else if (!check2) {
            return C.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 == C.HEAD;
        bool check2 = _newPrice < orders[asset][_nextId].price || _nextId == C.TAIL;

        if (check1 && check2) {
            return C.EXACT;
        } else if (!check1) {
            return C.PREV;
        } else if (!check2) {
            return C.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][C.HEAD].prevId;

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

        emit Events.CancelOrder(asset, id, order.orderType);
        // create the links using the other side of the HEAD
        _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][C.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
        orders[asset][C.HEAD].prevId = id;
        // @dev mark as cancelled instead of deleting the order itself
        STypes.Order storage order = orders[asset][id];
        order.orderType = cancelledOrMatched;
        // 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 != C.HEAD) {
            order.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 <-> ...
            order.prevId = C.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 price Price of the incomingOrder
     * @param orderType Ordertype of the incomingOrder
     * @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,
        uint256 price,
        O orderType,
        uint16 hintId
    ) internal view returns (uint16) {
        STypes.Order storage hintOrder = orders[asset][hintId];
        uint16 nextId = hintOrder.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, C.NEXT, C.HEAD, price, orderType);
        }

        // check if the hint is valid
        int256 direction = verifyId(orders, asset, hintId, price, nextId, 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 == C.EXACT) {
            return hintId;
        } else if (direction == C.NEXT) {
            return getOrderId(orders, asset, C.NEXT, nextId, price, orderType);
        } else {
            uint16 prevId = hintOrder.prevId;
            return getOrderId(orders, asset, C.PREV, prevId, price, 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) {
            STypes.Order storage hintOrder = orders[asset][hintId];
            uint16 nextId = hintOrder.nextId;

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

            if (direction == C.PREV) {
                uint16 prevId = hintOrder.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 asset The market that will be impacted
     * @param id Most recent matched Bid
     * @param isOrderFullyFilled Boolean to see if full or partial
     */
    function updateBidOrdersOnMatch(address asset, uint16 id, bool isOrderFullyFilled) internal {
        AppStorage storage s = appStorage();
        // BEFORE: HEAD <-> ... <-> (ID) <-> NEXT
        // AFTER : HEAD <------------------> NEXT
        if (isOrderFullyFilled) {
            _updateOrders(s.bids, asset, C.HEAD, id);
        } else {
            // BEFORE: HEAD <-> ... <-> (ID)
            // AFTER : HEAD <---------> (ID)
            s.bids[asset][C.HEAD].nextId = id;
            s.bids[asset][id].prevId = C.HEAD;
        }
    }

    /**
     * @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, C.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) {
                // @dev 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 != C.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();
        STypes.Order storage bidHEAD = s.bids[asset][C.HEAD];
        uint16 highestBidId = bidHEAD.nextId;

        if (incomingAsk.price > s.bids[asset][highestBidId].price) {
            addSellOrder(incomingAsk, asset, orderHintArray);
            return;
        }
        // matching loop starts
        MTypes.Match memory matchTotal;
        while (true) {
            STypes.Order storage highestBid = s.bids[asset][highestBidId];
            uint256 highestBidPrice = highestBid.price;
            uint256 highestBidErcAmount = highestBid.ercAmount;
            if (incomingAsk.price <= highestBidPrice) {
                // Consider ask filled if only dust amount left
                if (incomingAsk.ercAmount.mul(highestBidPrice) == 0) {
                    updateBidOrdersOnMatch(asset, highestBidId, false);
                    incomingAsk.ercAmount = 0;
                    matchIncomingSell(asset, incomingAsk, matchTotal);
                    return;
                }
                matchHighestBid(incomingAsk, highestBid, asset, matchTotal);
                if (incomingAsk.ercAmount > highestBidErcAmount) {
                    incomingAsk.ercAmount -= uint88(highestBidErcAmount); // @dev(safe-cast)
                    matchOrder(s.bids, asset, highestBidId);

                    // loop
                    highestBidId = highestBid.nextId;
                    if (highestBidId == C.TAIL) {
                        if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
                            addSellOrder(incomingAsk, asset, orderHintArray);
                        } else {
                            // @dev relevant for short orders to set SR status
                            incomingAsk.ercAmount = 0;
                        }

                        matchIncomingSell(asset, incomingAsk, matchTotal);

                        bidHEAD.nextId = C.TAIL;
                        return;
                    }
                } else {
                    if (incomingAsk.ercAmount == highestBidErcAmount) {
                        matchOrder(s.bids, asset, highestBidId);
                        updateBidOrdersOnMatch(asset, highestBidId, true);
                    } else {
                        highestBidErcAmount -= incomingAsk.ercAmount;
                        highestBid.ercAmount = uint88(highestBidErcAmount); // @dev(safe-cast)
                        updateBidOrdersOnMatch(asset, highestBidId, false);
                        // Check reduced dust threshold for existing limit orders
                        if (highestBidErcAmount.mul(highestBidPrice) < LibAsset.minBidEth(asset).mul(C.DUST_FACTOR)) {
                            cancelBid(asset, highestBidId);
                        }
                    }
                    incomingAsk.ercAmount = 0;
                    matchIncomingSell(asset, incomingAsk, matchTotal);
                    return;
                }
            } else {
                updateBidOrdersOnMatch(asset, highestBidId, false);

                if (incomingAsk.ercAmount.mul(incomingAsk.price) >= minAskEth) {
                    addSellOrder(incomingAsk, asset, orderHintArray);
                } else {
                    // @dev relevant for short orders to set SR status
                    incomingAsk.ercAmount = 0;
                }

                matchIncomingSell(asset, incomingAsk, matchTotal);

                return;
            }
        }
    }

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

        emit Events.MatchOrder(asset, incomingOrder.addr, incomingOrder.orderType, matchTotal.fillEth, matchTotal.fillErc);

        if (o == O.LimitShort) {
            matchIncomingShort(asset, incomingOrder, matchTotal);
        } else if (o == O.LimitAsk) {
            matchIncomingAsk(asset, incomingOrder, matchTotal);
        }

        // @dev match price is based on the order that was already on orderbook
        LibPriceDiscount.handlePriceDiscount(asset, matchTotal.lastMatchPrice, matchTotal.fillErc);
    }

    /**
     * @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();

        address asker = incomingAsk.addr;
        uint256 vault = s.asset[asset].vault;
        STypes.AssetUser storage assetUser = s.assetUser[asset][asker];
        STypes.VaultUser storage vaultUser = s.vaultUser[vault][asker];
        s.vault[vault].dittoMatchedShares += matchTotal.dittoMatchedShares;
        vaultUser.ethEscrowed += matchTotal.fillEth;
        assetUser.ercEscrowed -= matchTotal.fillErc;
    }

    /**
     * @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 {
        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 -= uint88(matchTotal.colUsed); // @dev(safe-cast)
        matchTotal.fillEth += uint88(matchTotal.colUsed); // @dev(safe-cast

        SR status;
        if (incomingShort.ercAmount == 0) {
            // @dev can happen if partially matched short order is not added to order book bc under dust threshold
            if (matchTotal.fillErc < LibAsset.minShortErc(Asset)) revert Errors.ShortRecordFullyFilledUnderMinSize();
            status = SR.FullyFilled;
        } else {
            status = SR.PartialFill;
        }

        STypes.ShortRecord storage shortRecord = s.shortRecords[asset][incomingShort.addr][incomingShort.shortRecordId];
        uint88 ethInitial = LibShortRecord.fillShortRecord(
            shortRecord, status, matchTotal.fillEth, matchTotal.fillErc, Asset.ercDebtRate, Vault.dethYieldRate
        );

        matchTotal.fillEth += ethInitial;

        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);

        // @dev Prevent exploiters from gaining Ditto by matching at prices far from oracle
        if (!priceDeviation(highestBid.price, LibOracle.getPrice(asset), 0.05 ether)) {
            increaseSharesOnMatch(asset, highestBid, matchTotal, fillEth);
        }

        if (incomingSell.orderType == O.LimitShort) {
            matchTotal.colUsed += incomingSell.price.mul(fillErc).mul(convertCR(incomingSell.shortOrderCR));
        }
        matchTotal.fillErc += fillErc;
        matchTotal.fillEth += fillEth;
        matchTotal.lastMatchPrice = highestBid.price;

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

    function _updateOracleAndStartingShort(address asset, uint256 savedPrice, uint16[] memory shortHintArray) private {
        AppStorage storage s = appStorage();
        uint256 oraclePrice = LibOracle.getOraclePrice(asset);
        asset.setPriceAndTime(oraclePrice, getOffsetTime());
        if (oraclePrice == savedPrice) {
            return; // no need to update startingShortId
        }

        STypes.Asset storage Asset = s.asset[asset];
        bool shortOrdersIsEmpty = s.shorts[asset][C.HEAD].nextId == C.TAIL;
        if (shortOrdersIsEmpty) {
            Asset.startingShortId = C.HEAD;
        } else {
            uint16 shortHintId;
            for (uint256 i = 0; i < shortHintArray.length;) {
                shortHintId = shortHintArray[i];
                unchecked {
                    ++i;
                }

                STypes.Order storage short = s.shorts[asset][shortHintId];
                if (short.orderType != O.LimitShort) continue;

                uint16 prevId = short.prevId;
                uint256 prevShortPrice = s.shorts[asset][prevId].price;
                uint256 shortPrice = short.price;

                bool isExactStartingShort = shortPrice >= oraclePrice && prevShortPrice < oraclePrice;
                if (isExactStartingShort) {
                    Asset.startingShortId = shortHintId;
                    return;
                }

                // @dev force hint to be within 0.5% of oraclePrice
                bool startingShortWithinOracleRange = shortPrice <= oraclePrice.mul(1.005 ether) && prevShortPrice >= oraclePrice;
                if (startingShortWithinOracleRange) {
                    // @dev prevShortPrice >= oraclePrice
                    Asset.startingShortId = prevId;
                    return;
                }

                bool allShortUnderOraclePrice = shortPrice < oraclePrice && short.nextId == C.TAIL;
                if (allShortUnderOraclePrice) {
                    Asset.startingShortId = C.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,
        STypes.Order memory incomingOrder,
        uint16[] memory shortHintArray
    ) internal {
        uint256 savedPrice = LibOracle.getPrice(asset);
        uint256 price = incomingOrder.price;
        // @dev handle .5% deviations in either directions
        if (priceDeviation(price, savedPrice, 0.005 ether)) {
            _updateOracleAndStartingShort(asset, savedPrice, shortHintArray);
        }
    }

    // @dev Possible for this function to never get called if updateOracleAndStartingShortViaThreshold() gets called often enough
    function updateOracleAndStartingShortViaTimeBidOnly(address asset, uint16[] memory shortHintArray) internal {
        uint256 timeDiff = getOffsetTime() - LibOracle.getTime(asset);
        if (timeDiff >= 15 minutes) {
            uint256 savedPrice = LibOracle.getPrice(asset);
            _updateOracleAndStartingShort(asset, savedPrice, shortHintArray);
        }
    }

    function updateStartingShortIdViaShort(address asset, STypes.Order memory incomingShort) internal {
        AppStorage storage s = appStorage();

        uint256 savedPrice = LibOracle.getPrice(asset);
        STypes.Asset storage Asset = s.asset[asset];
        uint16 startingShortId = Asset.startingShortId;
        uint256 startingShortPrice = s.shorts[asset][startingShortId].price;
        bool shortOrdersIsEmpty = s.shorts[asset][C.HEAD].nextId == C.TAIL;

        if (shortOrdersIsEmpty || startingShortId == C.HEAD) {
            if (incomingShort.price >= savedPrice) {
                Asset.startingShortId = incomingShort.id;
            }
        } else if (incomingShort.price < startingShortPrice && incomingShort.price >= savedPrice) {
            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) {
        AppStorage storage s = appStorage();
        bool anyOrderHintPrevMatched;
        bool shortOrderHintPrevMatched;

        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) {
                shortOrderHintPrevMatched = hintOrderType == O.LimitShort;
                anyOrderHintPrevMatched = true;
            }
        }

        if (shortOrderHintPrevMatched) {
            // @dev If order was short and hint was prev matched, assume that hint was close to startingShortId
            return s.asset[asset].startingShortId;
        }
        if (anyOrderHintPrevMatched) {
            // @dev If hint was prev matched, assume that hint was close to HEAD and therefore is reasonable to use HEAD
            return C.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);
    }

    // @dev MUST check for invalidShortOrder before calling this function
    function cancelShort(address asset, uint16 id) internal {
        AppStorage storage s = appStorage();

        STypes.Order storage shortOrder = s.shorts[asset][id];
        uint256 cr = convertCR(shortOrder.shortOrderCR);
        uint88 eth = shortOrder.ercAmount.mulU88(shortOrder.price).mulU88(cr);
        uint8 shortRecordId = shortOrder.shortRecordId;
        address shorter = shortOrder.addr;

        STypes.ShortRecord storage shortRecord = s.shortRecords[asset][shorter][shortRecordId];

        STypes.Asset storage Asset = s.asset[asset];
        uint256 vault = Asset.vault;
        if (shortRecord.status == SR.Closed) {
            // Return ethInitial if applicable
            if (shortRecord.collateral > 0) {
                eth += shortRecord.collateral;
            }
            // @dev creating shortOrder automatically creates a closed shortRecord which also sets a shortRecordId
            // @dev cancelling an unmatched order needs to also handle/recycle the shortRecordId
            LibShortRecord.deleteShortRecord(asset, shorter, shortRecordId);
        } else {
            uint256 minShortErc = LibAsset.minShortErc(Asset);
            if (shortRecord.ercDebt < minShortErc) {
                // @dev prevents leaving behind a partially filled SR under minShortErc
                // @dev if the corresponding short is cancelled, then the partially filled SR's debt will == minShortErc
                {
                    uint88 debtDiff = uint88(minShortErc - shortRecord.ercDebt); // @dev(safe-cast)
                    {
                        STypes.Vault storage Vault = s.vault[vault];

                        uint88 collateralDiff = shortOrder.price.mulU88(debtDiff).mulU88(cr);

                        LibShortRecord.fillShortRecord(
                            shortRecord, SR.FullyFilled, collateralDiff, debtDiff, Asset.ercDebtRate, Vault.dethYieldRate
                        );

                        Vault.dethCollateral += collateralDiff;
                        Asset.dethCollateral += collateralDiff;
                        Asset.ercDebt += debtDiff;

                        // @dev update the eth refund amount
                        eth -= collateralDiff;
                    }
                    // @dev virtually mint the increased debt
                    s.assetUser[asset][shorter].ercEscrowed += debtDiff;
                }

                // @dev Need to check resulting CR when cancel initiated by shorter in case of unfavorable price change
                if (shorter == msg.sender) {
                    if (LibShortRecord.getCollateralRatio(shortRecord, LibOracle.getPrice(asset)) < LibAsset.initialCR(Asset)) {
                        revert Errors.ShortRecordLacksMinCollateral();
                    }
                }
            } else {
                shortRecord.status = SR.FullyFilled;
            }
        }

        s.vaultUser[vault][shorter].ethEscrowed += eth;

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

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

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    function priceDeviation(uint256 price1, uint256 price2, uint256 pctDiff) internal pure returns (bool priceDeviationThreshold) {
        if (price1 >= price2) {
            priceDeviationThreshold = ((price1 - price2).div(price2)) > pctDiff;
        } else {
            priceDeviationThreshold = ((price2 - price1).div(price2)) > pctDiff;
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

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

library LibAsset {
    using U256 for uint256;
    using U88 for uint88;

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

    function getAssetCollateralRatio(STypes.Asset storage Asset, uint256 oraclePrice) internal view returns (uint256 assetCR) {
        return Asset.dethCollateral.div(oraclePrice.mul(Asset.ercDebt));
    }

    // 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(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.initialCR) * 1 ether) / C.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 liquidationCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].liquidationCR) * 1 ether) / C.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 liquidationCR
    // @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) / C.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 penaltyCR(address asset) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.asset[asset].penaltyCR) * 1 ether) / C.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) / C.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) / C.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) / C.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(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.minAskEth) * 1 ether) / C.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(STypes.Asset storage Asset) internal view returns (uint256) {
        return uint256(Asset.minShortErc) * 1 ether;
    }

    // default of 1.5 ether, stored in uint8 as 150
    // range of [1-2],
    // 2 decimal places, divide by 100
    // i.e. 120 -> 1.2 ether
    // @dev cRatio where the market enters recovery mode
    function recoveryCR(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.recoveryCR) * 1 ether) / C.TWO_DECIMAL_PLACES;
    }

    // default of .001 ether, stored in uint16 as 10
    // range of [1 - 10000],
    // 4 decimal places, divide by 10000
    // i.e. 120 -> .012 ether
    // @dev penalty fee applied to ercDebtRate when assets are traded at discount
    function discountPenaltyFee(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.discountPenaltyFee) * 1 ether) / C.FOUR_DECIMAL_PLACES;
    }

    // default of 10 ether, stored in uint16 as 10000
    // range of [1 - 65535],
    // 4 decimal places, divide by 1000
    // i.e. 5000 -> 5 ether
    // @dev multiplier applied to discount when increasing discountedErcMatched
    function discountMultiplier(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.discountMultiplier) * 1 ether) / C.THREE_DECIMAL_PLACES;
    }

    // default of 2.0 ether, stored in uint8 as 20
    // range of [1-10],
    // 1 decimal place, divide by 10
    // i.e. 12 -> 1.2 ether
    // @dev cRatio where a SR can be proposed for redemption
    function redemptionCR(STypes.Asset storage Asset) internal view returns (uint256) {
        return (uint256(Asset.redemptionCR) * 1 ether) / C.ONE_DECIMAL_PLACES;
    }
}

File 33 of 53 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";

/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(SD1x18.unwrap(x)));
}

/// @notice Casts an SD1x18 number into UD2x18.
/// - x must be positive.
function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD2x18_Underflow(x);
    }
    result = UD2x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
    }
    result = uint256(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
    }
    result = uint128(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD1x18 x) pure returns (uint40 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Underflow(x);
    }
    if (xInt > int64(uint64(Common.MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Overflow(x);
    }
    result = uint40(uint64(xInt));
}

/// @notice Alias for {wrap}.
function sd1x18(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

/// @notice Unwraps an SD1x18 number into int64.
function unwrap(SD1x18 x) pure returns (int64 result) {
    result = SD1x18.unwrap(x);
}

/// @notice Wraps an int64 number into SD1x18.
function wrap(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

File 34 of 53 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Casts an SD59x18 number into int256.
/// @dev This is basically a functional alias for {unwrap}.
function intoInt256(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Casts an SD59x18 number into SD1x18.
/// @dev Requirements:
/// - x must be greater than or equal to `uMIN_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < uMIN_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
    }
    if (xInt > uMAX_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xInt));
}

/// @notice Casts an SD59x18 number into UD2x18.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
    }
    if (xInt > int256(uint256(uMAX_UD2x18))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(uint256(xInt)));
}

/// @notice Casts an SD59x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD59x18 x) pure returns (UD60x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD59x18 x) pure returns (uint256 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
    }
    result = uint256(xInt);
}

/// @notice Casts an SD59x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UINT128`.
function intoUint128(SD59x18 x) pure returns (uint128 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT128))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
    }
    result = uint128(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD59x18 x) pure returns (uint40 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
    }
    result = uint40(uint256(xInt));
}

/// @notice Alias for {wrap}.
function sd(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Alias for {wrap}.
function sd59x18(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Unwraps an SD59x18 number into int256.
function unwrap(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Wraps an int256 number into SD59x18.
function wrap(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

File 35 of 53 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(-x.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(-x.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 36 of 53 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uEXP_MIN_THRESHOLD,
    uEXP2_MIN_THRESHOLD,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_SD59x18,
    uMAX_WHOLE_SD59x18,
    uMIN_SD59x18,
    uMIN_WHOLE_SD59x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { wrap } from "./Helpers.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Calculates the absolute value of x.
///
/// @dev Requirements:
/// - x must be greater than `MIN_SD59x18`.
///
/// @param x The SD59x18 number for which to calculate the absolute value.
/// @param result The absolute value of x as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function abs(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Abs_MinSD59x18();
    }
    result = xInt < 0 ? wrap(-xInt) : x;
}

/// @notice Calculates the arithmetic average of x and y.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The arithmetic average as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    unchecked {
        // This operation is equivalent to `x / 2 +  y / 2`, and it can never overflow.
        int256 sum = (xInt >> 1) + (yInt >> 1);

        if (sum < 0) {
            // If at least one of x and y is odd, add 1 to the result, because shifting negative numbers to the right
            // rounds toward negative infinity. The right part is equivalent to `sum + (x % 2 == 1 || y % 2 == 1)`.
            assembly ("memory-safe") {
                result := add(sum, and(or(xInt, yInt), 1))
            }
        } else {
            // Add 1 if both x and y are odd to account for the double 0.5 remainder truncated after shifting.
            result = wrap(sum + (xInt & yInt & 1));
        }
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt > uMAX_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Ceil_Overflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt > 0) {
                resultInt += uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Divides two SD59x18 numbers, returning a new SD59x18 number.
///
/// @dev This is an extension of {Common.mulDiv} for signed numbers, which works by computing the signs and the absolute
/// values separately.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The denominator must not be zero.
/// - The result must fit in SD59x18.
///
/// @param x The numerator as an SD59x18 number.
/// @param y The denominator as an SD59x18 number.
/// @param result The quotient as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Div_InputTooSmall();
    }

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

    // Compute the absolute value (x*UNIT÷y). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv(xAbs, uint256(uUNIT), yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Div_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}.
///
/// Requirements:
/// - Refer to the requirements in {exp2}.
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();

    // Any input less than the threshold returns zero.
    // This check also prevents an overflow for very small numbers.
    if (xInt < uEXP_MIN_THRESHOLD) {
        return ZERO;
    }

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xInt > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        int256 doubleUnitProduct = xInt * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method using the following formula:
///
/// $$
/// 2^{-x} = \frac{1}{2^x}
/// $$
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Notes:
/// - If x is less than -59_794705707972522261, the result is zero.
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in SD59x18.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        // The inverse of any number less than the threshold is truncated to zero.
        if (xInt < uEXP2_MIN_THRESHOLD) {
            return ZERO;
        }

        unchecked {
            // Inline the fixed-point inversion to save gas.
            result = wrap(uUNIT_SQUARED / exp2(wrap(-xInt)).unwrap());
        }
    } else {
        // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
        if (xInt > uEXP2_MAX_INPUT) {
            revert Errors.PRBMath_SD59x18_Exp2_InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x_192x64 = uint256((xInt << 64) / uUNIT);

            // It is safe to cast the result to int256 due to the checks above.
            result = wrap(int256(Common.exp2(x_192x64)));
        }
    }
}

/// @notice Yields the greatest whole number less than or equal to x.
///
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be greater than or equal to `MIN_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < uMIN_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Floor_Underflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt < 0) {
                resultInt -= uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right.
/// of the radix point for negative numbers.
/// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
/// @param x The SD59x18 number to get the fractional part of.
/// @param result The fractional part of x as an SD59x18 number.
function frac(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % uUNIT);
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x * y must fit in SD59x18.
/// - x * y must not be negative, since complex numbers are not supported.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == 0 || yInt == 0) {
        return ZERO;
    }

    unchecked {
        // Equivalent to `xy / x != y`. Checking for overflow this way is faster than letting Solidity do it.
        int256 xyInt = xInt * yInt;
        if (xyInt / xInt != yInt) {
            revert Errors.PRBMath_SD59x18_Gm_Overflow(x, y);
        }

        // The product must not be negative, since complex numbers are not supported.
        if (xyInt < 0) {
            revert Errors.PRBMath_SD59x18_Gm_NegativeProduct(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        uint256 resultUint = Common.sqrt(uint256(xyInt));
        result = wrap(int256(resultUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The SD59x18 number for which to calculate the inverse.
/// @return result The inverse as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(uUNIT_SQUARED / x.unwrap());
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(SD59x18 x) pure returns (SD59x18 result) {
    // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
    // {log2} can return is ~195_205294292027477728.
    result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this block is the standard multiplication operation, not {SD59x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        default { result := uMAX_SD59x18 }
    }

    if (result.unwrap() == uMAX_SD59x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation.
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The SD59x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt <= 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    unchecked {
        int256 sign;
        if (xInt >= uUNIT) {
            sign = 1;
        } else {
            sign = -1;
            // Inline the fixed-point inversion to save gas.
            xInt = uUNIT_SQUARED / xInt;
        }

        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(uint256(xInt / uUNIT));

        // This is the integer part of the logarithm as an SD59x18 number. The operation can't overflow
        // because n is at most 255, `UNIT` is 1e18, and the sign is either 1 or -1.
        int256 resultInt = int256(n) * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        int256 y = xInt >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultInt * sign);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        int256 DOUBLE_UNIT = 2e18;
        for (int256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultInt = resultInt + delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        resultInt *= sign;
        result = wrap(resultInt);
    }
}

/// @notice Multiplies two SD59x18 numbers together, returning a new SD59x18 number.
///
/// @dev Notes:
/// - Refer to the notes in {Common.mulDiv18}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv18}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The result must fit in SD59x18.
///
/// @param x The multiplicand as an SD59x18 number.
/// @param y The multiplier as an SD59x18 number.
/// @return result The product as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Mul_InputTooSmall();
    }

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

    // Compute the absolute value (x*y÷UNIT). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv18(xAbs, yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Mul_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Raises x to the power of y using the following formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}, {log2}, and {mul}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as an SD59x18 number.
/// @param y Exponent to raise x to, as an SD59x18 number
/// @return result x raised to power y, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xInt == 0) {
        return yInt == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xInt == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yInt == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yInt == uUNIT) {
        return x;
    }

    // Calculate the result using the formula.
    result = exp2(mul(log2(x), y));
}

/// @notice Raises x (an SD59x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {abs} and {Common.mulDiv18}.
/// - The result must fit in SD59x18.
///
/// @param x The base as an SD59x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(SD59x18 x, uint256 y) pure returns (SD59x18 result) {
    uint256 xAbs = uint256(abs(x).unwrap());

    // Calculate the first iteration of the loop in advance.
    uint256 resultAbs = y & 1 > 0 ? xAbs : uint256(uUNIT);

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    uint256 yAux = y;
    for (yAux >>= 1; yAux > 0; yAux >>= 1) {
        xAbs = Common.mulDiv18(xAbs, xAbs);

        // Equivalent to `y % 2 == 1`.
        if (yAux & 1 > 0) {
            resultAbs = Common.mulDiv18(resultAbs, xAbs);
        }
    }

    // The result must fit in SD59x18.
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Powu_Overflow(x, y);
    }

    unchecked {
        // Is the base negative and the exponent odd? If yes, the result should be negative.
        int256 resultInt = int256(resultAbs);
        bool isNegative = x.unwrap() < 0 && y & 1 == 1;
        if (isNegative) {
            resultInt = -resultInt;
        }
        result = wrap(resultInt);
    }
}

/// @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:
/// - Only the positive root is returned.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x cannot be negative, since complex numbers are not supported.
/// - x must be less than `MAX_SD59x18 / UNIT`.
///
/// @param x The SD59x18 number for which to calculate the square root.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Sqrt_NegativeInput(x);
    }
    if (xInt > uMAX_SD59x18 / uUNIT) {
        revert Errors.PRBMath_SD59x18_Sqrt_Overflow(x);
    }

    unchecked {
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two SD59x18 numbers.
        // In this case, the two numbers are both the square root.
        uint256 resultUint = Common.sqrt(uint256(xInt * uUNIT));
        result = wrap(int256(resultUint));
    }
}

File 37 of 53 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";

/// @notice Casts a UD2x18 number into SD1x18.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(uMAX_SD1x18)) {
        revert Errors.PRBMath_UD2x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xUint));
}

/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}

/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
    result = uint128(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
    result = uint256(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD2x18 x) pure returns (uint40 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(Common.MAX_UINT40)) {
        revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
    result = UD2x18.unwrap(x);
}

/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

File 38 of 53 : Events.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

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

    event DepositEth(address indexed bridge, address indexed user, uint256 amount);
    event Deposit(address indexed bridge, address indexed user, uint256 amount);
    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 srId, uint256 amount);
    event DecreaseCollateral(address indexed asset, address indexed user, uint8 srId, uint256 amount);
    event CombineShorts(address indexed asset, address indexed user, uint8[] srIds);

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

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

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

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

    event ProposeRedemption(address indexed asset, address indexed redeemer);
    event DisputeRedemptionAll(address indexed asset, address indexed redeemer);
    event ClaimRedemption(address indexed asset, address indexed redeemer);

    event UpdateYield(uint256 indexed vault);
    event DistributeYield(uint256 indexed vault, address indexed user, uint256 yieldAmount, 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(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);
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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 {C} 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);
        AggregatorV3Interface oracle = AggregatorV3Interface(s.asset[asset].oracle);
        if (address(oracle) == address(0)) revert Errors.InvalidAsset();

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

                oracleCircuitBreaker(roundID, baseRoundID, price, basePrice, timeStamp, baseTimeStamp);
                uint256 priceInEth = uint256(price).div(uint256(basePrice));
                return priceInEth;
            }
        } catch {
            if (oracle == baseOracle) {
                return twapCircuitBreaker();
            } else {
                (
                    uint80 roundID,
                    int256 price,
                    /*uint256 startedAt*/
                    ,
                    uint256 timeStamp,
                    /*uint80 answeredInRound*/
                ) = oracle.latestRoundData();
                if (validateFetchData(roundID, timeStamp, price)) revert Errors.InvalidPrice();

                uint256 twapInv = twapCircuitBreaker();
                uint256 priceInEth = uint256(price * C.BASE_ORACLE_DECIMALS).mul(twapInv);
                return priceInEth;
            }
        }
    }

    function baseOracleCircuitBreaker(
        uint256 protocolPrice,
        uint80 roundId,
        int256 chainlinkPrice,
        uint256 timeStamp,
        uint256 chainlinkPriceInEth
    ) private view returns (uint256 _protocolPrice) {
        // @dev block.timestamp > 2 hours + baseTimeStamp applies only to baseTimeStamp (eth/usd)
        // @dev Other asset oracles have different heartbeats or update rates per Chainlink
        bool invalidFetchData = validateFetchData(roundId, timeStamp, chainlinkPrice) || 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. Verify twap and compare with chainlink
        if (invalidFetchData) {
            return twapCircuitBreaker();
        } else if (priceDeviation) {
            // Check valid twap price
            try IDiamond(payable(address(this))).estimateWETHInUSDC(C.UNISWAP_WETH_BASE_AMT, 30 minutes) returns (uint256 twapPrice)
            {
                if (twapPrice == 0) {
                    return chainlinkPriceInEth;
                }

                uint256 twapPriceNormalized = twapPrice * (1 ether / C.DECIMAL_USDC);
                uint256 twapPriceInEth = twapPriceNormalized.inv();
                uint256 twapDiff = twapPriceInEth > protocolPrice ? twapPriceInEth - protocolPrice : protocolPrice - twapPriceInEth;

                // Save the price that is closest to saved oracle price
                if (chainlinkDiff <= twapDiff) {
                    return chainlinkPriceInEth;
                } else {
                    // Check valid twap liquidity
                    IERC20 weth = IERC20(C.WETH);
                    uint256 wethBal = weth.balanceOf(C.USDC_WETH);
                    if (wethBal < 100 ether) {
                        return chainlinkPriceInEth;
                    }
                    return twapPriceInEth;
                }
            } catch {
                return chainlinkPriceInEth;
            }
        } else {
            return chainlinkPriceInEth;
        }
    }

    function oracleCircuitBreaker(
        uint80 roundId,
        uint80 baseRoundId,
        int256 chainlinkPrice,
        int256 baseChainlinkPrice,
        uint256 timeStamp,
        uint256 baseTimeStamp
    ) private view {
        bool invalidFetchData = validateFetchData(roundId, timeStamp, chainlinkPrice);
        bool invalidFetchDataBase =
            validateFetchData(baseRoundId, baseTimeStamp, baseChainlinkPrice) || block.timestamp > 2 hours + baseTimeStamp;
        if (invalidFetchData || invalidFetchDataBase) revert Errors.InvalidPrice();
    }

    function twapCircuitBreaker() private view returns (uint256 twapPriceInEth) {
        // Check valid price
        uint256 twapPrice = IDiamond(payable(address(this))).estimateWETHInUSDC(C.UNISWAP_WETH_BASE_AMT, 30 minutes);
        if (twapPrice == 0) revert Errors.InvalidTwapPrice();

        // Check valid liquidity
        IERC20 weth = IERC20(C.WETH);
        uint256 wethBal = weth.balanceOf(C.USDC_WETH);
        if (wethBal < 100 ether) revert Errors.InsufficientEthInLiquidityPool();

        uint256 twapPriceNormalized = twapPrice * (1 ether / C.DECIMAL_USDC);
        return twapPriceNormalized.inv();
    }

    /* 
    @dev C.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][C.HEAD].ercAmount = uint80(oraclePrice);
        s.bids[asset][C.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][C.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][C.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);
        }
    }

    function validateFetchData(uint80 roundId, uint256 timeStamp, int256 chainlinkPrice)
        private
        view
        returns (bool invalidFetchData)
    {
        invalidFetchData = roundId == 0 || timeStamp == 0 || timeStamp > block.timestamp || chainlinkPrice <= 0;
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

import {U256, U104, U88} from "contracts/libraries/PRBMathHelper.sol";
import {IDiamond} from "interfaces/IDiamond.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {STypes, MTypes} from "contracts/libraries/DataTypes.sol";
import {LibAsset} from "contracts/libraries/LibAsset.sol";
import {LibOracle} from "contracts/libraries/LibOracle.sol";
import {C} from "contracts/libraries/Constants.sol";

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

library LibPriceDiscount {
    using LibOracle for address;
    using U256 for uint256;
    using U104 for uint104;
    using U88 for uint88;

    // Approximates the match price compared to the oracle price and accounts for any discount by increasing ercDebtRate
    function handlePriceDiscount(address asset, uint256 price, uint256 ercAmount) internal {
        AppStorage storage s = appStorage();
        MTypes.HandleDiscount memory h;
        h.asset = asset;
        STypes.Asset storage Asset = s.asset[h.asset];
        h.ercDebt = Asset.ercDebt;
        h.price = price;
        h.ercAmount = ercAmount;
        // @dev No need to consider discounts if system-wide ercDebt is low
        if (h.ercDebt <= C.DISCOUNT_UPDATE_THRESHOLD) return;
        h.savedPrice = LibOracle.getPrice(h.asset);
        // @dev Applying penalty to ercDebt when asset level CR is low is harmful
        uint256 assetCR = LibAsset.getAssetCollateralRatio(Asset, h.savedPrice);
        if (assetCR <= LibAsset.recoveryCR(Asset)) return;
        // @dev Only consider discounts that are meaningfully different from oracle price
        if (h.savedPrice > h.price.mul(1 ether + C.DISCOUNT_THRESHOLD)) {
            IDiamond(payable(address(this)))._matchIsDiscounted(h);
        } else {
            if (Asset.discountedErcMatched > h.ercAmount) {
                Asset.discountedErcMatched -= uint88(h.ercAmount); // @dev(safe-cast)
            } else {
                Asset.discountedErcMatched = 1 wei;
            }
            // @dev Reset iniitialDiscountTime to reset the daysMultiplier
            Asset.initialDiscountTime = 1 seconds;
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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 {C} from "contracts/libraries/Constants.sol";
import {LibBridgeRouter} from "contracts/libraries/LibBridgeRouter.sol";
import {LibOrders} from "contracts/libraries/LibOrders.sol";

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

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

    function getCollateralRatio(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][C.HEAD].nextId;
        if (id <= C.HEAD) {
            return 0;
        }

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

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

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

        STypes.ShortRecord storage shortRecord = s.shortRecords[asset][shorter][id];
        shortRecord.prevId = C.HEAD;
        shortRecord.id = id;
        shortRecord.nextId = nextId;
        shortRecord.status = status;
        shortRecord.collateral = collateral;
        shortRecord.ercDebt = ercAmount;
        shortRecord.ercDebtRate = ercDebtRate;
        shortRecord.dethYieldRate = dethYieldRate;
        shortRecord.updatedAt = LibOrders.getOffsetTime();
        shortRecord.ercDebtFee = ercDebtFee;

        emit Events.CreateShortRecord(asset, shorter, id);
    }

    // @dev No need for ercDebtFee because it's 0 whenever this function is called 
    function fillShortRecord(
        STypes.ShortRecord storage short,
        SR status,
        uint88 collateral,
        uint88 ercAmount,
        uint80 ercDebtRate,
        uint80 dethYieldRate
    ) internal returns (uint88 ethInitial) {
        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.dethYieldRate = dethYieldRate;
            short.updatedAt = LibOrders.getOffsetTime();
            // This is the one exception in the case of seeded collateral for capital efficient SR
            ethInitial = short.collateral;
            short.collateral += collateral;
        } else {
            uint256 ercDebtSocialized = ercAmount.mul(ercDebtRate);
            uint256 yield = collateral.mul(dethYieldRate);
            merge(short, ercAmount, ercDebtSocialized, collateral, yield, LibOrders.getOffsetTime(), 0);
        }
        // @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
        if (shortRecord.status != SR.PartialFill && shorter != address(this)) {
            // 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 != C.HEAD) {
                s.shortRecords[asset][shorter][shortRecord.nextId].prevId = shortRecord.prevId;
            }
            // Make reuseable for future short records
            uint8 prevHEAD = s.shortRecords[asset][shorter][C.HEAD].prevId;
            s.shortRecords[asset][shorter][C.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 > C.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 = C.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;
        // @dev Necessary for seeding closed SR for capital efficient SR
        shortRecord.collateral = 0;
    }

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

        STypes.ShortRecord storage headSR = s.shortRecords[asset][shorter][C.HEAD];
        STypes.AssetUser storage AssetUser = s.assetUser[asset][shorter];
        // Initialize HEAD in case of first short createShortRecord
        if (AssetUser.shortRecordCounter == 0) {
            AssetUser.shortRecordCounter = C.SHORT_STARTING_ID;
            headSR.prevId = C.HEAD;
            headSR.nextId = C.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 > C.HEAD) {
            // BEFORE: CancelledID <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
            // AFTER1: CancelledID <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
            uint8 prevCanceledId = s.shortRecords[asset][shorter][canceledId].prevId;
            if (prevCanceledId > C.HEAD) {
                headSR.prevId = prevCanceledId;
            } else {
                // BEFORE: HEAD <- (ID) <- HEAD <-> .. <-> PREV <----------> NEXT
                // AFTER1: HEAD <--------- HEAD <-> .. <-> PREV <-> [ID] <-> NEXT
                headSR.prevId = C.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.shortRecordCounter;
            // Avoids overflow revert, prevents DOS on uint8
            if (id < C.SHORT_MAX_ID) {
                AssetUser.shortRecordCounter += 1;
            } else {
                revert Errors.CannotMakeMoreThanMaxSR();
            }
        }

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

    function merge(
        STypes.ShortRecord storage short,
        uint88 ercDebt,
        uint256 ercDebtSocialized,
        uint88 collateral,
        uint256 yield,
        uint32 creationTime,
        uint88 ercDebtFee
    ) internal {
        // Resolve ercDebt
        if (ercDebt > 0 || ercDebtSocialized > 0) {
            ercDebtSocialized += (short.ercDebt - short.ercDebtFee).mul(short.ercDebtRate);
            short.ercDebt += ercDebt;
            short.ercDebtFee += ercDebtFee;
            short.ercDebtRate = ercDebtSocialized.divU64(short.ercDebt - short.ercDebtFee);
        }

        // Resolve dethCollateral
        yield += short.collateral.mul(short.dethYieldRate);
        short.collateral += collateral;
        short.dethYieldRate = yield.divU80(short.collateral);
        // Assign updatedAt
        short.updatedAt = creationTime;
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

import {STypes} from "contracts/libraries/DataTypes.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {C} from "contracts/libraries/Constants.sol";

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

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

library LibVault {
    using U256 for uint256;
    using U88 for uint88;
    using {dethTithePercent} for uint256;

    // default of .1 ether, stored in uint16 as 10_00
    // range of [0-100],
    // i.e. 12.34% as 12_34 / 10_000 -> 0.1234 ether
    // @dev percentage of yield given to TAPP
    function dethTithePercent(uint256 vault) internal view returns (uint256) {
        AppStorage storage s = appStorage();

        return (uint256(s.vault[vault].dethTithePercent) * 1 ether) / C.FOUR_DECIMAL_PLACES;
    }

    // default of 19 ether, stored in uint16 as 19
    // range of [0-100],
    // i.e. 19 -> 0.19 ether
    // @dev per second rate of ditto tokens released to shorters
    // @dev 19 per second -> 5_991_840 per year
    function dittoShorterRate(uint256 vault) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.vault[vault].dittoShorterRate) * 1 ether) / C.TWO_DECIMAL_PLACES;
    }

    // default of 19 ether, stored in uint16 as 19
    // range of [0-100],
    // i.e. 19 -> 0.19 ether
    // @dev per second rate of ditto tokens released to qualifying matched orders
    // @dev 19 per second -> 5_991_840 per year
    function dittoMatchedRate(uint256 vault) internal view returns (uint256) {
        AppStorage storage s = appStorage();
        return (uint256(s.vault[vault].dittoMatchedRate) * 1 ether) / C.TWO_DECIMAL_PLACES;
    }

    // Loops through each bridge in the vault and totals present value
    function getDethTotal(uint256 vault) internal view returns (uint256 dethTotal) {
        AppStorage storage s = appStorage();
        address[] storage bridges = s.vaultBridges[vault];
        uint256 bridgeCount = bridges.length;

        for (uint256 i; i < bridgeCount;) {
            dethTotal += IBridge(bridges[i]).getDethValue();
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Updates the vault yield rate from staking rewards earned by bridge contracts holding LSD
     * @dev Does not distribute yield to any individual owner of shortRecords
     *
     * @param vault The vault that will be impacted
     */
    function updateYield(uint256 vault) internal {
        AppStorage storage s = appStorage();

        STypes.Vault storage Vault = s.vault[vault];
        STypes.VaultUser storage TAPP = s.vaultUser[vault][address(this)];
        // Retrieve vault variables
        uint88 dethTotalNew = uint88(getDethTotal(vault)); // @dev(safe-cast)
        uint88 dethTotal = Vault.dethTotal;
        uint88 dethCollateral = Vault.dethCollateral;
        uint88 dethTreasury = TAPP.ethEscrowed;

        // Pass yield if > 0
        if (dethTotalNew <= dethTotal) return;
        uint88 yield = dethTotalNew - dethTotal;

        // If no short records, yield goes to treasury
        if (dethCollateral == 0) {
            TAPP.ethEscrowed += yield;
            Vault.dethTotal = dethTotalNew;
            return;
        }

        // Assign yield to dethTreasury
        uint88 dethTreasuryReward = yield.mul(dethTreasury).divU88(dethTotal);
        yield -= dethTreasuryReward;
        // Assign tithe of the remaining yield to treasuryF
        uint88 tithe = yield.mulU88(vault.dethTithePercent());
        yield -= tithe;
        // Calculate change in yield rate
        uint80 dethYieldRate = yield.divU80(dethCollateral);
        if (dethYieldRate == 0) return;
        // Realize new totals if yield rate increases after rounding
        TAPP.ethEscrowed += dethTreasuryReward + tithe;
        Vault.dethTotal = dethTotalNew;
        Vault.dethYieldRate += dethYieldRate;
        Vault.dethCollateralReward += yield;
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;



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 44 of 53 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);

File 45 of 53 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

/// @notice Thrown when taking the absolute value of `MIN_SD59x18`.
error PRBMath_SD59x18_Abs_MinSD59x18();

/// @notice Thrown when ceiling a number overflows SD59x18.
error PRBMath_SD59x18_Ceil_Overflow(SD59x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMath_SD59x18_Convert_Overflow(int256 x);

/// @notice Thrown when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMath_SD59x18_Convert_Underflow(int256 x);

/// @notice Thrown when dividing two numbers and one of them is `MIN_SD59x18`.
error PRBMath_SD59x18_Div_InputTooSmall();

/// @notice Thrown when dividing two numbers and one of the intermediary unsigned results overflows SD59x18.
error PRBMath_SD59x18_Div_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_SD59x18_Exp_InputTooBig(SD59x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_SD59x18_Exp2_InputTooBig(SD59x18 x);

/// @notice Thrown when flooring a number underflows SD59x18.
error PRBMath_SD59x18_Floor_Underflow(SD59x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and their product is negative.
error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18.
error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18.
error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256.
error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x);

/// @notice Thrown when taking the logarithm of a number less than or equal to zero.
error PRBMath_SD59x18_Log_InputTooSmall(SD59x18 x);

/// @notice Thrown when multiplying two numbers and one of the inputs is `MIN_SD59x18`.
error PRBMath_SD59x18_Mul_InputTooSmall();

/// @notice Thrown when multiplying two numbers and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Mul_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when raising a number to a power and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Powu_Overflow(SD59x18 x, uint256 y);

/// @notice Thrown when taking the square root of a negative number.
error PRBMath_SD59x18_Sqrt_NegativeInput(SD59x18 x);

/// @notice Thrown when the calculating the square root overflows SD59x18.
error PRBMath_SD59x18_Sqrt_Overflow(SD59x18 x);

File 46 of 53 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.
error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.
error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);

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

// 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 49 of 53 : IDiamond.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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/migration-redeem-vault/ThrowAwayFacet.sol
  function getBaseOracle() external view returns (address);
  function getFlaggerIdCounter() external view returns (uint24);
  function getTokenIdCounter() external view returns (uint40);
  function getReentrantStatus() external view returns (uint8);
  function getDethVault(address deth) external view returns (uint256);
  function getFlagMapping(uint24 flaggerId) external view returns (address);
  function getFiller1() external view returns (uint256);
  function getFiller2() external view returns (uint256);
  function getFiller3() external view returns (uint256);
  function getNFTName() external view returns (string memory);
  function getNFTSymbol() external view returns (string memory);
  function v2StorageMigration(address yDUSD) external;
  // functions from contracts/facets/ViewRedemptionFacet.sol
  function getTimeToDispute(uint256 lastCR) external view returns (uint32 timeToDispute);
  function getRedemptionFee(address asset, uint88 ercDebtRedeemed, uint88 colRedeemed) external view returns (uint88 redemptionFee);
  function readProposalData(address asset, address redeemer) external view returns (uint32, uint32, uint80, uint80, MTypes.ProposalData[] memory);
  // functions from contracts/facets/OwnerFacet.sol
  function createMarket(address asset, address yieldVault, 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 createVault(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 setLiquidationCR(address asset, uint16 value) external;
  function setForcedBidPriceBuffer(address asset, uint8 value) external;
  function setPenaltyCR(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 setRecoveryCR(address asset, uint8 value) external;
  function setDiscountPenaltyFee(address asset, uint16 value) external;
  function setDiscountMultiplier(address asset, uint16 value) external;
  function setYieldVault(address asset, address vault) external;
  function setRedemptionCR(address asset, uint8 value) external;
  function createBridge(address bridge, uint256 vault, uint16 withdrawalFee) external;
  function setWithdrawalFee(address bridge, uint16 withdrawalFee) external;
  // functions from contracts/facets/PrimaryLiquidationFacet.sol
  function liquidate(address asset, address shorter, uint8 id, uint16[] memory shortHintArray, uint16 shortOrderId) 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/ProposeRedemptionFacet.sol
  function proposeRedemption(
        address asset, MTypes.ProposalInput[] calldata proposalInput, uint88 redemptionAmount, uint88 maxRedemptionFee, uint256 deadline) external;
  // functions from contracts/facets/DisputeRedemptionFacet.sol
  function disputeRedemption(address asset, address redeemer, uint8 incorrectIndex, address disputeShorter, uint8 disputeShortId) external;
  // functions from contracts/facets/DiamondEtherscanFacet.sol
  function setDummyImplementation(address _implementation) external;
  function implementation() external view returns (address);
  // 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 getCollateralRatioLiquidation(address asset, address shorter, uint8 shortRecordId, uint16 shortOrderId) 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 getShortOrderId(address asset, address shorter, uint8 shortRecordId) external view returns (uint16 shortOrderId);
  function getShortOrderIdArray(address asset, address shorter, uint8[] memory shortRecordIds) external view returns (uint16[] memory shortOrderIds);
  function getMinShortErc(address asset) external view returns (uint256);
  function getTimeSinceDiscounted(address asset) external view returns (uint32 timeSinceLastDiscount);
  function getInitialDiscountTime(address asset) external view returns (uint32 initialDiscountTime);
  function getExpectedSRDebt(address asset, address shorter, uint8 id) external view returns (uint88 updatedErcDebt);
  // 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 setLiquidationCRT(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 getAssetNormalizedStruct(address asset) external view returns (TestTypes.AssetNormalizedStruct memory);
  function getBridgeNormalizedStruct(address bridge) external view returns (TestTypes.BridgeNormalizedStruct memory);
  function getWithdrawalFeePct(uint256 bridgePointer, address rethBridge, address stethBridge) external view returns (uint256);
  function setBaseOracle(address _oracle) external;
  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 setDethYieldRate(uint256 vault, uint256 value) external;
  function nonZeroVaultSlot0(uint256 vault) external;
  function setforcedBidPriceBufferT(address asset, uint8 value) external;
  function setErcDebtRateAsset(address asset, uint64 value) external;
  function setOrderIdT(address asset, uint16 value) external;
  function setEthEscrowed(address addr, uint88 eth) external;
  function setBridgeCredit(address addr, uint88 bridgeCreditReth, uint88 bridgeCreditSteth) external;
  function getUserOrders(address asset, address addr, O orderType) external view returns (STypes.Order[] memory orders);
  function getAssets() external view returns (address[] memory);
  function dittoShorterRate(uint256 vault) external view returns (uint256);
  function dittoMatchedRate(uint256 vault) external view returns (uint256);
  function deleteBridge(address bridge) external;
  function setAssetOracle(address asset, address oracle) external;
  function setErcDebt(address asset, address shorter, uint8 id, uint88 value) external;
  function setErcDebtAsset(address asset, uint88 value) external;
  function setDiscountedErcMatchedAsset(address asset, uint104 value) external;
  function setInitialDiscountTimeAsset(address asset, uint32 value) external;
  function addErcDebtAsset(address asset, uint88 value) external;
  function setLastRedemptionTime(address asset, uint32 lastRedemptionTime) external;
  function setBaseRate(address asset, uint64 baseRate) external;
  function setMinShortErcT(address asset, uint16 value) external;
  function addErcDebtFee(address asset, address shorter, uint8 id, uint88 value) external;
  function set_tstore(bytes32 slot, uint256 val) external;
  function get_tload(bytes32 slot) external view returns (uint256 val);
  function getYieldVault(address asset) external view returns (address);
  // 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 withdrawTapp(address bridge, uint88 dethAmount) external;
  // functions from contracts/facets/ExitShortFacet.sol
  function exitShortWallet(address asset, uint8 id, uint88 buybackAmount, uint16 shortOrderId) external;
  function exitShortErcEscrowed(address asset, uint8 id, uint88 buybackAmount, uint16 shortOrderId) external;
  function exitShort(
        address asset, uint8 id, uint88 buybackAmount, uint80 price, uint16[] memory shortHintArray, uint16 shortOrderId) 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, uint16[] memory shortOrderIds) 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;
  function _matchIsDiscounted(MTypes.HandleDiscount memory h) external;
  // functions from contracts/facets/ShortOrdersFacet.sol
  function createLimitShort(
        address asset, uint80 price, uint88 ercAmount, MTypes.OrderHint[] memory orderHintArray, uint16[] memory shortHintArray, uint16 shortOrderCR) external;
  // functions from contracts/facets/ClaimRedemptionFacet.sol
  function claimRedemption(address asset) external;
  function claimRemainingCollateral(address asset, address redeemer, uint8 claimIndex, uint8 id) external;
  // 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 depositAsset(address asset, uint104 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;
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

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

import {STypes} from "contracts/libraries/DataTypes.sol";
import {AppStorage, appStorage} from "contracts/libraries/AppStorage.sol";
import {OracleLibrary} from "contracts/libraries/UniswapOracleLibrary.sol";
import {C, VAULT} from "contracts/libraries/Constants.sol";
import {Errors} from "contracts/libraries/Errors.sol";

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

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

library LibBridgeRouter {
    using U256 for uint256;
    using U88 for uint88;

    // Credit user account with dETH and bridge credit if applicable
    function addDeth(uint256 vault, uint256 bridgePointer, uint88 amount) internal {
        AppStorage storage s = appStorage();
        STypes.VaultUser storage VaultUser = s.vaultUser[vault][msg.sender];

        if (vault == VAULT.ONE) {
            // Only VAULT.ONE has mixed LST
            if (bridgePointer == VAULT.BRIDGE_RETH) {
                VaultUser.bridgeCreditReth += amount;
            } else {
                VaultUser.bridgeCreditSteth += amount;
            }
        }

        VaultUser.ethEscrowed += amount;
        s.vault[vault].dethTotal += amount;
    }

    // Determine how much dETH is NOT covered by bridge credits during withdrawal
    function assessDeth(uint256 vault, uint256 bridgePointer, uint88 amount, address rethBridge, address stethBridge)
        internal
        returns (uint88)
    {
        AppStorage storage s = appStorage();
        STypes.VaultUser storage VaultUser = s.vaultUser[vault][msg.sender];

        uint88 creditReth;
        uint88 creditSteth;
        if (bridgePointer == VAULT.BRIDGE_RETH) {
            // Withdraw RETH
            creditReth = VaultUser.bridgeCreditReth;
            if (creditReth >= amount) {
                VaultUser.bridgeCreditReth -= amount;
                return 0;
            }

            VaultUser.bridgeCreditReth = 0;
            amount -= creditReth;
            creditSteth = VaultUser.bridgeCreditSteth;
            if (creditSteth < C.ROUNDING_ZERO) {
                // Valid withdraw when no STETH credits
                return amount;
            } else {
                if (IBridge(stethBridge).getDethValue() < C.ROUNDING_ZERO) {
                    // Can withdraw RETH using STETH credit when STETH bridge is empty
                    if (creditSteth >= amount) {
                        VaultUser.bridgeCreditSteth -= amount;
                        return 0;
                    } else {
                        VaultUser.bridgeCreditSteth = 0;
                        return amount - creditSteth;
                    }
                } else {
                    // Must use available bridge credits on withdrawal
                    // @dev Prevents abusing bridge for arbitrage
                    revert Errors.MustUseExistingBridgeCredit();
                }
            }
        } else {
            // Withdraw STETH
            creditSteth = VaultUser.bridgeCreditSteth;
            if (creditSteth >= amount) {
                VaultUser.bridgeCreditSteth -= amount;
                return 0;
            }

            VaultUser.bridgeCreditSteth = 0;
            amount -= creditSteth;
            creditReth = VaultUser.bridgeCreditReth;
            if (creditReth < C.ROUNDING_ZERO) {
                // Valid withdraw when no RETH credits
                return amount;
            } else {
                if (IBridge(rethBridge).getDethValue() < C.ROUNDING_ZERO) {
                    // Can withdraw STETH using RETH credit when RETH bridge is empty
                    if (creditReth >= amount) {
                        VaultUser.bridgeCreditReth -= amount;
                        return 0;
                    } else {
                        VaultUser.bridgeCreditReth = 0;
                        return amount - creditReth;
                    }
                } else {
                    // Must use available bridge credits on withdrawal
                    // @dev Prevents abusing bridge for arbitrage
                    revert Errors.MustUseExistingBridgeCredit();
                }
            }
        }
    }

    // Bridge fees exist only to prevent free arbitrage, fee charged is the premium/discount differential
    // @dev Only applicable to VAULT.ONE which has mixed LST
    function withdrawalFeePct(uint256 bridgePointer, address rethBridge, address stethBridge) internal view returns (uint256 fee) {
        IBridge bridgeReth = IBridge(rethBridge);
        IBridge bridgeSteth = IBridge(stethBridge);

        // Calculate rETH market premium/discount (factor)
        uint256 unitRethTWAP = OracleLibrary.estimateTWAP(1 ether, 30 minutes, VAULT.RETH_WETH, VAULT.RETH, C.WETH);
        uint256 unitRethOracle = bridgeReth.getUnitDethValue();
        uint256 factorReth = unitRethTWAP.div(unitRethOracle);
        // Calculate stETH market premium/discount (factor)
        uint256 unitWstethTWAP = OracleLibrary.estimateTWAP(1 ether, 30 minutes, VAULT.WSTETH_WETH, VAULT.WSTETH, C.WETH);
        uint256 unitWstethOracle = bridgeSteth.getUnitDethValue();
        uint256 factorSteth = unitWstethTWAP.div(unitWstethOracle);
        if (factorReth > factorSteth) {
            // rETH market premium relative to stETH
            if (bridgePointer == VAULT.BRIDGE_RETH) {
                // Only charge fee if withdrawing rETH
                return factorReth.div(factorSteth) - 1 ether;
            }
        } else if (factorSteth > factorReth) {
            // stETH market premium relative to rETH
            if (bridgePointer == VAULT.BRIDGE_STETH) {
                // Only charge fee if withdrawing stETH
                return factorSteth.div(factorReth) - 1 ether;
            }
        } else {
            // Withdrawing less premium LST or premiums are equivalent
            return 0;
        }
    }

    // Update user account upon dETH withdrawal
    function removeDeth(uint256 vault, uint88 amount, uint88 fee) internal {
        AppStorage storage s = appStorage();
        s.vaultUser[vault][msg.sender].ethEscrowed -= (amount + fee);
        s.vault[vault].dethTotal -= amount;
    }
}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.26;

interface IBridge {
    error NotDiamond();
    error NetBalanceZero();

    function getBaseCollateral() external view returns (address);
    function getDethValue() external view returns (uint256);
    function getUnitDethValue() external view returns (uint256);
    function deposit(address, uint256) external returns (uint256);
    function depositEth() external payable returns (uint256);
    function withdraw(address, uint256) external returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.26;

// https://github.com/Uniswap/v3-periphery/blob/b325bb0905d922ae61fcc7df85ee802e8df5e96c/contracts/libraries/OracleLibrary.sol

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

interface IUniswapV3Pool {
    function observe(uint32[] calldata secondsAgos)
        external
        view
        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
}

/* solhint-disable */

/// @title Oracle library
/// @notice Provides functions to integrate with V3 pool oracle
library OracleLibrary {
    /// @notice Given a tick and a token amount, calculates the amount of token received in exchange
    /// @param tick Tick value used to calculate the quote
    /// @param baseAmount Amount of token to be converted
    /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination
    /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination
    /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken
    function getQuoteAtTick(int24 tick, uint128 baseAmount, address baseToken, address quoteToken)
        internal
        pure
        returns (uint256 quoteAmount)
    {
        uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);

        // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself
        if (sqrtRatioX96 <= type(uint128).max) {
            uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
            quoteAmount =
                baseToken < quoteToken ? U256.mulDiv(ratioX192, baseAmount, 1 << 192) : U256.mulDiv(1 << 192, baseAmount, ratioX192);
        } else {
            uint256 ratioX128 = U256.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);
            quoteAmount =
                baseToken < quoteToken ? U256.mulDiv(ratioX128, baseAmount, 1 << 128) : U256.mulDiv(1 << 128, baseAmount, ratioX128);
        }
    }

    function estimateTWAP(uint128 amountIn, uint32 secondsAgo, address pool, address baseToken, address quoteToken)
        internal
        view
        returns (uint256 amountOut)
    {
        if (secondsAgo <= 0) revert Errors.InvalidTWAPSecondsAgo();

        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = secondsAgo;
        secondsAgos[1] = 0;

        // @dev Returns the cumulative tick and liquidity as of each timestamp secondsAgo from the current block timestamp
        (int56[] memory tickCumulatives,) = IUniswapV3Pool(pool).observe(secondsAgos);

        int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
        int24 tick = int24(tickCumulativesDelta / int32(secondsAgo));

        // Always round to negative infinity
        if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int32(secondsAgo) != 0)) {
            tick--;
        }

        // @dev Gets price using this formula: p(i) = 1.0001**i, where i is the tick
        amountOut = getQuoteAtTick(tick, amountIn, baseToken, quoteToken);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.26;

// https://github.com/Uniswap/v3-core/blob/6562c52e8f75f0c10f9deaf44861847585fc8129/contracts/libraries/TickMath.sol

/* solhint-disable */
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    error T();
    error R();

    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        unchecked {
            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
            if (absTick > uint256(int256(MAX_TICK))) revert T();

            uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
            if (absTick & 0x2 != 0) {
                ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
            }
            if (absTick & 0x4 != 0) {
                ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
            }
            if (absTick & 0x8 != 0) {
                ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
            }
            if (absTick & 0x10 != 0) {
                ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
            }
            if (absTick & 0x20 != 0) {
                ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
            }
            if (absTick & 0x40 != 0) {
                ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
            }
            if (absTick & 0x80 != 0) {
                ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
            }
            if (absTick & 0x100 != 0) {
                ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
            }
            if (absTick & 0x200 != 0) {
                ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
            }
            if (absTick & 0x400 != 0) {
                ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
            }
            if (absTick & 0x800 != 0) {
                ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
            }
            if (absTick & 0x1000 != 0) {
                ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
            }
            if (absTick & 0x2000 != 0) {
                ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
            }
            if (absTick & 0x4000 != 0) {
                ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
            }
            if (absTick & 0x8000 != 0) {
                ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
            }
            if (absTick & 0x10000 != 0) {
                ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
            }
            if (absTick & 0x20000 != 0) {
                ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
            }
            if (absTick & 0x40000 != 0) {
                ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
            }
            if (absTick & 0x80000 != 0) {
                ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
            }

            if (tick > 0) ratio = type(uint256).max / ratio;

            // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
            // ...then downcast because it is known the result always fits within 160 bits due to the tick input constraint
            // ...round up in the division so getTickAtSqrtRatio of the output price is always consistent
            sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
        }
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        unchecked {
            // second inequality must be < because the price can never reach the price at the max tick
            if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) {
                revert R();
            }
            uint256 ratio = uint256(sqrtPriceX96) << 32;

            uint256 r = ratio;
            uint256 msb = 0;

            assembly {
                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(5, gt(r, 0xFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(4, gt(r, 0xFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(3, gt(r, 0xFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(2, gt(r, 0xF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(1, gt(r, 0x3))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := gt(r, 0x1)
                msb := or(msb, f)
            }

            if (msb >= 128) r = ratio >> (msb - 127);
            else r = ratio << (127 - msb);

            int256 log_2 = (int256(msb) - 128) << 64;

            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(63, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(62, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(61, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(60, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(59, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(58, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(57, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(56, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(55, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(54, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(53, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(52, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(51, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(50, f))
            }

            int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

            int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
            int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

            tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
        }
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"NotOwnerOrAdmin","type":"error"},{"inputs":[],"name":"getBaseOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deth","type":"address"}],"name":"getDethVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFiller1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFiller2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFiller3","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"flaggerId","type":"uint24"}],"name":"getFlagMapping","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFlaggerIdCounter","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNFTName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNFTSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReentrantStatus","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenIdCounter","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yDUSD","type":"address"}],"name":"v2StorageMigration","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080604052348015600e575f80fd5b50610a9c8061001c5f395ff3fe608060405234801561000f575f80fd5b50600436106100cf575f3560e01c8063788a547c1161007d578063b6cef37311610058578063b6cef3731461020d578063c490a30a14610215578063dbfd4b981461024a575f80fd5b8063788a547c146101c35780637fa4c0e2146101fd5780638e32987014610205575f80fd5b806330d9f828116100ad57806330d9f8281461014257806350273b2d146101575780636422c94b1461018f575f80fd5b806306c9c6a7146100d35780632a218cbe146100f15780633088794e14610103575b5f80fd5b6100db610285565b6040516100e8919061090c565b60405180910390f35b6010545b6040519081526020016100e8565b60025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e8565b61015561015036600461095f565b610317565b005b6002547c0100000000000000000000000000000000000000000000000000000000900460ff1660405160ff90911681526020016100e8565b60025474010000000000000000000000000000000000000000900462ffffff1660405162ffffff90911681526020016100e8565b61011d6101d1366004610999565b62ffffff165f908152600e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6011546100f5565b6100db6108a9565b600f546100f5565b6100f561022336600461095f565b73ffffffffffffffffffffffffffffffffffffffff165f9081526003602052604090205490565b60025477010000000000000000000000000000000000000000000000900464ffffffffff1660405164ffffffffff90911681526020016100e8565b60605f6015018054610296906109bb565b80601f01602080910402602001604051908101604052809291908181526020018280546102c2906109bb565b801561030d5780601f106102e45761010080835404028352916020019161030d565b820191905f5260205f20905b8154815290600101906020018083116102f057829003601f168201915b5050505050905090565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5462010000900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156103a757505f5473ffffffffffffffffffffffffffffffffffffffff163314155b156103de576040517fdce3812500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73d177000a2bc4f4d2246f0527ad74fd4140e029fd5f81905260136020527f0907fe4bd0d0ed17ff95eada1144502430a8009c91bcdade9800bb5a7d3f3bee5473d1770004661852cbc0b317c7775f4fa22e6bc60a90309073ffffffffffffffffffffffffffffffffffffffff8086169116036104bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f616c72656164792072756e000000000000000000000000000000000000000000604482015260640160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8381165f818152600860209081526040808320600190810180547fffffffffffffffffffffffff00ffffffffffffffffffffffff00ffffffffffff167396000000000000000000000000320000000000001781556013845282852080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168c891617905580547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff169055600d83528184209587168085529583528184206002855283528184208101805471024a68ee00000000000000000000000000007fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161790559383526009825280832094835293815292812080546cffffffffffffffffffffffffff166d030000000000000000000000000017905590815260069091527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054829061064457610644610a0c565b5f918252602080832090910154600180845260069092527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a31805473ffffffffffffffffffffffffffffffffffffffff9092169450919081106106a8576106a8610a0c565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff858116845260048352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000090811690915591909216808552918420805490911690556001909252600690527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054919250908061074f5761074f610a39565b5f828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559092019092556001909152600690527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054806107e3576107e3610a39565b5f828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055909201909255600280547fffffff000000000000000000ffffffffffffffffffffffffffffffffffffffff16905573ffffffffffffffffffffffffffffffffffffffff861682526003905260408120819055610896906014906108ba565b6108a160155f6108ba565b505050505050565b60605f6014018054610296906109bb565b5080546108c6906109bb565b5f825580601f106108d5575050565b601f0160209004905f5260205f20908101906108f191906108f4565b50565b5b80821115610908575f81556001016108f5565b5090565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f6020828403121561096f575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610992575f80fd5b9392505050565b5f602082840312156109a9575f80fd5b813562ffffff81168114610992575f80fd5b600181811c908216806109cf57607f821691505b602082108103610a06577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220edbce458536143d5031527c24064fcb3a0c6d9859f695454c66b379054a1cf9564736f6c634300081a0033

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106100cf575f3560e01c8063788a547c1161007d578063b6cef37311610058578063b6cef3731461020d578063c490a30a14610215578063dbfd4b981461024a575f80fd5b8063788a547c146101c35780637fa4c0e2146101fd5780638e32987014610205575f80fd5b806330d9f828116100ad57806330d9f8281461014257806350273b2d146101575780636422c94b1461018f575f80fd5b806306c9c6a7146100d35780632a218cbe146100f15780633088794e14610103575b5f80fd5b6100db610285565b6040516100e8919061090c565b60405180910390f35b6010545b6040519081526020016100e8565b60025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e8565b61015561015036600461095f565b610317565b005b6002547c0100000000000000000000000000000000000000000000000000000000900460ff1660405160ff90911681526020016100e8565b60025474010000000000000000000000000000000000000000900462ffffff1660405162ffffff90911681526020016100e8565b61011d6101d1366004610999565b62ffffff165f908152600e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6011546100f5565b6100db6108a9565b600f546100f5565b6100f561022336600461095f565b73ffffffffffffffffffffffffffffffffffffffff165f9081526003602052604090205490565b60025477010000000000000000000000000000000000000000000000900464ffffffffff1660405164ffffffffff90911681526020016100e8565b60605f6015018054610296906109bb565b80601f01602080910402602001604051908101604052809291908181526020018280546102c2906109bb565b801561030d5780601f106102e45761010080835404028352916020019161030d565b820191905f5260205f20905b8154815290600101906020018083116102f057829003601f168201915b5050505050905090565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5462010000900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156103a757505f5473ffffffffffffffffffffffffffffffffffffffff163314155b156103de576040517fdce3812500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73d177000a2bc4f4d2246f0527ad74fd4140e029fd5f81905260136020527f0907fe4bd0d0ed17ff95eada1144502430a8009c91bcdade9800bb5a7d3f3bee5473d1770004661852cbc0b317c7775f4fa22e6bc60a90309073ffffffffffffffffffffffffffffffffffffffff8086169116036104bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f616c72656164792072756e000000000000000000000000000000000000000000604482015260640160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8381165f818152600860209081526040808320600190810180547fffffffffffffffffffffffff00ffffffffffffffffffffffff00ffffffffffff167396000000000000000000000000320000000000001781556013845282852080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168c891617905580547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff169055600d83528184209587168085529583528184206002855283528184208101805471024a68ee00000000000000000000000000007fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161790559383526009825280832094835293815292812080546cffffffffffffffffffffffffff166d030000000000000000000000000017905590815260069091527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054829061064457610644610a0c565b5f918252602080832090910154600180845260069092527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a31805473ffffffffffffffffffffffffffffffffffffffff9092169450919081106106a8576106a8610a0c565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff858116845260048352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000090811690915591909216808552918420805490911690556001909252600690527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054919250908061074f5761074f610a39565b5f828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559092019092556001909152600690527f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a318054806107e3576107e3610a39565b5f828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055909201909255600280547fffffff000000000000000000ffffffffffffffffffffffffffffffffffffffff16905573ffffffffffffffffffffffffffffffffffffffff861682526003905260408120819055610896906014906108ba565b6108a160155f6108ba565b505050505050565b60605f6014018054610296906109bb565b5080546108c6906109bb565b5f825580601f106108d5575050565b601f0160209004905f5260205f20908101906108f191906108f4565b50565b5b80821115610908575f81556001016108f5565b5090565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f6020828403121561096f575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610992575f80fd5b9392505050565b5f602082840312156109a9575f80fd5b813562ffffff81168114610992575f80fd5b600181811c908216806109cf57607f821691505b602082108103610a06577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220edbce458536143d5031527c24064fcb3a0c6d9859f695454c66b379054a1cf9564736f6c634300081a0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.