ETH Price: $2,604.08 (-0.26%)

Contract

0xcbc0A8d5C7352Fe3625614ea343019e6d6b89031
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Borrow209590122024-10-13 20:22:594 days ago1728850979IN
0xcbc0A8d5...6d6b89031
0 ETH0.0107825926.44744845
Repay All209563582024-10-13 11:29:234 days ago1728818963IN
0xcbc0A8d5...6d6b89031
0 ETH0.0035673310.18703503
Add Collateral209451192024-10-11 21:40:476 days ago1728682847IN
0xcbc0A8d5...6d6b89031
0 ETH0.000601449.7
Borrow209131802024-10-07 10:47:2310 days ago1728298043IN
0xcbc0A8d5...6d6b89031
0 ETH0.002573999.9
Add Collateral209131752024-10-07 10:46:2310 days ago1728297983IN
0xcbc0A8d5...6d6b89031
0 ETH0.000589049.5
Add Collateral209127392024-10-07 9:18:5910 days ago1728292739IN
0xcbc0A8d5...6d6b89031
0 ETH0.000648668.2
Borrow208931262024-10-04 15:42:1113 days ago1728056531IN
0xcbc0A8d5...6d6b89031
0 ETH0.0047735717.23466993
Add Collateral208931172024-10-04 15:40:2313 days ago1728056423IN
0xcbc0A8d5...6d6b89031
0 ETH0.0008868514.30297047
Add Collateral208930792024-10-04 15:32:4713 days ago1728055967IN
0xcbc0A8d5...6d6b89031
0 ETH0.0009696112.25727241
Repay208863112024-10-03 16:55:2314 days ago1727974523IN
0xcbc0A8d5...6d6b89031
0 ETH0.0039387811.33936691
Repay208862822024-10-03 16:49:3514 days ago1727974175IN
0xcbc0A8d5...6d6b89031
0 ETH0.0044793812.83229803
Repay All207710192024-09-17 14:46:4730 days ago1726584407IN
0xcbc0A8d5...6d6b89031
0 ETH0.0056200415.3
Borrow207586582024-09-15 21:18:1132 days ago1726435091IN
0xcbc0A8d5...6d6b89031
0 ETH0.001474713.53677588
Borrow207563982024-09-15 13:45:1132 days ago1726407911IN
0xcbc0A8d5...6d6b89031
0 ETH0.000608612.33998827
Borrow207443342024-09-13 21:19:3534 days ago1726262375IN
0xcbc0A8d5...6d6b89031
0 ETH0.002683736.7
Borrow207420502024-09-13 13:39:5934 days ago1726234799IN
0xcbc0A8d5...6d6b89031
0 ETH0.001622643.8
Repay206729522024-09-03 22:10:2344 days ago1725401423IN
0xcbc0A8d5...6d6b89031
0 ETH0.001079473.06117315
Remove Collatera...206604172024-09-02 4:12:4745 days ago1725250367IN
0xcbc0A8d5...6d6b89031
0 ETH0.000152031
Repay All206260952024-08-28 9:09:1150 days ago1724836151IN
0xcbc0A8d5...6d6b89031
0 ETH0.000997382.7
Borrow206227522024-08-27 21:56:4751 days ago1724795807IN
0xcbc0A8d5...6d6b89031
0 ETH0.0049312511.72518875
Repay All205477682024-08-17 10:32:3561 days ago1723890755IN
0xcbc0A8d5...6d6b89031
0 ETH0.000837942.4
Repay205358762024-08-15 18:40:2363 days ago1723747223IN
0xcbc0A8d5...6d6b89031
0 ETH0.003145568.99999504
Borrow205352212024-08-15 16:27:3563 days ago1723739255IN
0xcbc0A8d5...6d6b89031
0 ETH0.004727311.2
Borrow205217222024-08-13 19:14:1165 days ago1723576451IN
0xcbc0A8d5...6d6b89031
0 ETH0.000365991.41211855
Add Collateral205217182024-08-13 19:13:2365 days ago1723576403IN
0xcbc0A8d5...6d6b89031
0 ETH0.000085181.37381582
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TempleLineOfCredit

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 999999 runs

Other Settings:
default evmVersion
File 1 of 46 : TempleLineOfCredit.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/templeLineOfCredit/TempleLineOfCredit.sol)

import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { mulDiv } from "@prb/math/src/Common.sol";

import { ITreasuryReservesVault } from "contracts/interfaces/v2/ITreasuryReservesVault.sol";
import { IInterestRateModel } from "contracts/interfaces/v2/interestRate/IInterestRateModel.sol";
import { ITempleLineOfCredit } from "contracts/interfaces/v2/templeLineOfCredit/ITempleLineOfCredit.sol";
import { ITlcStrategy } from "contracts/interfaces/v2/strategies/ITlcStrategy.sol";
import { CompoundedInterest } from "contracts/v2/interestRate/CompoundedInterest.sol";
import { ITempleCircuitBreakerProxy } from "contracts/interfaces/v2/circuitBreaker/ITempleCircuitBreakerProxy.sol";

import { SafeCast } from "contracts/common/SafeCast.sol";
import { CommonEventsAndErrors } from "contracts/common/CommonEventsAndErrors.sol";
import { TempleElevatedAccess } from "contracts/v2/access/TempleElevatedAccess.sol";

/* solhint-disable not-rely-on-time */

/**
 * @title Temple Line of Credit (TLC)
 * @notice Users supply Temple as collateral, and can then borrow DAI.
 * 
 * Temple is valued at the Temple Treasury Price Index (TPI)
 * User debt increases at a continuously compounding rate.
 * Liquidations occur when users LTV exceeds the maximum allowed.
 */
contract TempleLineOfCredit is ITempleLineOfCredit, TempleElevatedAccess {
    using SafeERC20 for IERC20;
    using SafeCast for uint256;
    using CompoundedInterest for uint256;

    /**
     * @notice The collateral token supplied by users/accounts
     */
    IERC20 public immutable override templeToken;

    /**
     * @notice DAI token -- the debt token which can be borrowed
     */
    IERC20 public immutable override daiToken;

    /**
     * @notice The Treasury Reserve Vault (TRV) which funds the DAI borrows to users/accounts.
     * - When users borrow, the DAI is pulled from the TRV
     *      (via the TlcStrategy, increasing the dUSD debt)
     * - When users repay, the DAI is repaid to the TRV 
     *      (reducing the dUSD debt of the TlcStrategy)
     * - When there is a liquidation, the seized Temple collateral is paid to the TRV
     *      (reducing the dTEMPLE debt of the TlcStrategy)
     */
    ITreasuryReservesVault public override treasuryReservesVault;

    /**
     * @notice The Strategy contract managing the TRV borrows and equity positions of TLC.
     */
    ITlcStrategy public override tlcStrategy;
    
    /**
     * @notice A record of the total amount of collateral deposited by users/accounts.
     */
    uint256 public override totalCollateral;

    /**
     * @notice A per user/account mapping to the data to track active collateral/debt positions.
     */
    mapping(address => AccountData) internal allAccountsData;

    /**
     * @notice Configuration and latest data snapshot of the debt tokens
     */
    DebtTokenConfig internal debtTokenConfig;
    DebtTokenData internal debtTokenData;

    /**
     * @notice Liquidations may be paused in order for users to recover/repay debt after emergency
     * actions
     */
    bool public override liquidationsPaused;

    /**
     * @notice The minimum borrow amount per transaction
     * @dev It costs gas to liquidate users, so we don't want dust amounts.
     */
    uint128 public override minBorrowAmount = 1000e18;

    /**
     * @notice New borrows and collateral withdrawals are checked against a circuit breaker
     * to ensure no more than a cap is withdrawn in a given period
     */
    ITempleCircuitBreakerProxy public immutable override circuitBreakerProxy;

    /**
     * @notice An internal state tracking how interest has accumulated.
     */
    uint256 internal constant INITIAL_INTEREST_ACCUMULATOR = 1e27;

    /**
     * @notice The precision used for Price, LTV, notional
     */
    uint256 internal constant PRECISION = 1e18;
    uint256 internal constant DOUBLE_PRECISION = 1e36;

    constructor(
        address _initialRescuer,
        address _initialExecutor,
        address _circuitBreakerProxy,
        address _templeToken,
        address _daiToken, 
        uint256 _maxLtvRatio,
        address _interestRateModel
    ) 
        TempleElevatedAccess(_initialRescuer, _initialExecutor)
    {
        circuitBreakerProxy = ITempleCircuitBreakerProxy(_circuitBreakerProxy);
        templeToken = IERC20(_templeToken);
        daiToken = IERC20(_daiToken);

        if (_maxLtvRatio > PRECISION) revert CommonEventsAndErrors.InvalidParam();
        if (_interestRateModel == address(0)) revert CommonEventsAndErrors.ExpectedNonZero();

        debtTokenConfig = DebtTokenConfig(
            uint96(_maxLtvRatio), 
            IInterestRateModel(_interestRateModel),
            false
        );
        debtTokenData = DebtTokenData({
            interestAccumulatorUpdatedAt: uint32(block.timestamp),
            totalDebt: 0,
            interestRate: 0,
            interestAccumulator: INITIAL_INTEREST_ACCUMULATOR
        });
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         COLLATERAL                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Deposit Temple as collateral
     * @param collateralAmount The amount to deposit
     * @param onBehalfOf An account can add collateral on behalf of another address.
     */
    function addCollateral(uint128 collateralAmount, address onBehalfOf) external override notInRescueMode {
        if (collateralAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        emit CollateralAdded(msg.sender, onBehalfOf, collateralAmount);

        allAccountsData[onBehalfOf].collateral += collateralAmount;
        totalCollateral += collateralAmount;

        // No need to check liquidity when adding collateral as it 
        // only improves the liquidity.
        templeToken.safeTransferFrom(
            msg.sender,
            address(this),
            collateralAmount 
        );
    }

    /**
     * @notice Remove Temple collateral. (active borrow positions are not allowed to go above the max LTV)
     * @param amount The amount of collateral to remove
     * @param recipient Send the Temple collateral to a specified recipient address.
     */
    function removeCollateral(uint128 amount, address recipient) external override notInRescueMode {
        if (amount == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        AccountData storage _accountData = allAccountsData[msg.sender];
        uint128 _collateral =  _accountData.collateral;
        if (amount > _collateral) revert CommonEventsAndErrors.InvalidAmount(address(templeToken), amount);

        // Ensure that this withdrawal doesn't break the circuit breaker limits (across all users)
        circuitBreakerProxy.preCheck(
            address(templeToken), 
            msg.sender, 
            amount
        );

        // Update the collateral, and then verify that it doesn't make the debt unsafe.
        _accountData.collateral = _collateral - amount;
        totalCollateral -= amount;
        emit CollateralRemoved(msg.sender, recipient, amount);

        _checkLiquidity(_accountData, _debtTokenCacheRO());

        templeToken.safeTransfer(
            recipient,
            amount
        );
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           BORROW                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Borrow DAI (not allowed to borrow over the max LTV)
     * @dev NOTICE: There is no buffer between the maximum the user is able to borrow 
     * (based on their collateral and the maxLTV), and the point where the user can get liquidated.
     * Therefore do not borrow the max amount possible or the position will instantly be eligable to be
     * liquidated in the next block.
     * @param amount The amount to borrow
     * @param recipient Send the borrowed token to a specified recipient address.
     */
    function borrow(uint128 amount, address recipient) external override notInRescueMode {
        if (amount < minBorrowAmount) revert InsufficientAmount(minBorrowAmount, amount);

        AccountData storage _accountData = allAccountsData[msg.sender];
        DebtTokenCache memory _cache = _debtTokenCache();
        if (_cache.config.borrowsPaused) revert Paused();

        // Ensure that this new borrow doesn't break the circuit breaker limits (across all users)
        circuitBreakerProxy.preCheck(
            address(daiToken), 
            msg.sender, 
            amount
        );

        // Apply the new borrow
        {
            uint128 _totalDebt = _currentAccountDebt(
                _cache, 
                _accountData.debtCheckpoint, 
                _accountData.interestAccumulator,
                false // don't round up on the way in
            ) + amount;

            // Update the state
            _accountData.debtCheckpoint = _totalDebt;
            _accountData.interestAccumulator = _cache.interestAccumulator;
            debtTokenData.totalDebt = _cache.totalDebt = _cache.totalDebt + amount;

            // Update the borrow interest rates based on the now increased utilization ratio
            _updateInterestRates(_cache);
        }

        emit Borrow(msg.sender, recipient, amount);
        _checkLiquidity(_accountData, _cache);

        // Finally, borrow the funds from the TRV and send the tokens to the recipient.
        tlcStrategy.fundFromTrv(amount, recipient);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            REPAY                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice An account repays some of its borrowed DAI debt
     * @param repayAmount The amount to repay. Cannot be more than the current debt.
     * @param onBehalfOf Another address can repay the debt on behalf of someone else
     */
    function repay(uint128 repayAmount, address onBehalfOf) external override notInRescueMode {
        if (repayAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero();

        AccountData storage _accountData = allAccountsData[onBehalfOf];
        DebtTokenCache memory _cache = _debtTokenCache();

        // Update the account's latest debt
        uint128 _newDebt = _currentAccountDebt(
            _cache, 
            _accountData.debtCheckpoint,
            _accountData.interestAccumulator,
            true // round up for repay balance
        );

        // They cannot repay more than this debt
        if (repayAmount > _newDebt) {
            revert ExceededBorrowedAmount(_newDebt, repayAmount);
        }
        unchecked {
            _newDebt -= repayAmount;
        }

        // Update storage
        _accountData.debtCheckpoint = _newDebt;
        _accountData.interestAccumulator = _cache.interestAccumulator;

        _repayToken(_cache, repayAmount, msg.sender, onBehalfOf);
    }

    /**
     * @notice An account repays all of its DAI debt
     * @dev The amount of debt is calculated as of this block.
     * @param onBehalfOf Another address can repay the debt on behalf of someone else
     */
    function repayAll(address onBehalfOf) external override notInRescueMode {
        DebtTokenCache memory _cache = _debtTokenCache();
        AccountData storage _accountData = allAccountsData[onBehalfOf];

        // Get the outstanding debt for Stable
        uint128 repayAmount = _currentAccountDebt(
            _cache,
            _accountData.debtCheckpoint,
            _accountData.interestAccumulator,
            true // use the rounded up amount
        );
        if (repayAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero();

        // Update storage
        _accountData.debtCheckpoint = 0;
        _accountData.interestAccumulator = _cache.interestAccumulator;

        _repayToken(_cache, repayAmount, msg.sender, onBehalfOf);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       LIQUIDATIONS                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Liquidate one or more accounts which have exceeded the 
     * maximum allowed LTV.
     * The Temple collateral is seized, and the accounts debt wiped.
     * @dev If one of the accounts in the batch hasn't exceeded the max LTV
     * then no action is performed for that account.
     */
    function batchLiquidate(
        address[] calldata accounts
    ) external override notInRescueMode returns (
        uint128 totalCollateralClaimed,
        uint128 totalDebtWiped
    ) {
        if (liquidationsPaused) revert Paused();

        LiquidationStatus memory _status;
        DebtTokenCache memory _cache = _debtTokenCache();
        address _account;
        uint256 _numAccounts = accounts.length;
        for (uint256 i; i < _numAccounts; ++i) {
            _account = accounts[i];
            _status = _computeLiquidity(
                allAccountsData[_account], 
                _cache
            );

            // Skip if this account is still under the maxLTV
            if (_status.hasExceededMaxLtv) {
                emit Liquidated(_account, _status.collateral, _status.collateralValue, _status.currentDebt);
                totalCollateralClaimed += _status.collateral;
                totalDebtWiped += _status.currentDebt;

                // Clear the account data
                delete allAccountsData[_account];
            }
        }

        // burn the temple collateral by repaying to TRV. This will burn the equivalent dTemple debt too.
        if (totalCollateralClaimed > 0) {
            templeToken.safeIncreaseAllowance(address(treasuryReservesVault), totalCollateralClaimed);
            treasuryReservesVault.repay(templeToken, totalCollateralClaimed, address(tlcStrategy));
            totalCollateral -= totalCollateralClaimed;
        }

        // Remove debt from the totals
        if (totalDebtWiped > 0) {
            _repayTotalDebt(_cache, totalDebtWiped);
        }
    }
    
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            ADMIN                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Liquidation may be paused in order for users to recover/repay debt after emergency
     * actions
     */
    function setLiquidationsPaused(bool isPaused) external override onlyElevatedAccess {
        liquidationsPaused = isPaused;
        emit LiquidationsPausedSet(isPaused);
    }

    /**
     * @notice Set the minimum amount of Temple which must be borrowed on each call.
     */
    function setMinBorrowAmount(uint128 amount) external override onlyElevatedAccess {
        minBorrowAmount = amount;
        emit MinBorrowAmountSet(amount);
    }

    /**
     * @notice Update the TLC Strategy contract, and Treasury Reserves Vault (TRV)
     * @dev The TRV is granted access to spend DAI, in order to repay debt.
     */
    function setTlcStrategy(
        address newTlcStrategy
    ) external override onlyElevatedAccess {
        tlcStrategy = ITlcStrategy(newTlcStrategy);

        // Remove allowance from the old TRV
        address previousTrv = address(treasuryReservesVault);
        if (previousTrv != address(0)) {
            daiToken.safeApprove(previousTrv, 0);
        }

        address _trv = address(tlcStrategy.treasuryReservesVault());
        treasuryReservesVault = ITreasuryReservesVault(_trv);

        // Set max allowance on the new TRV
        {
            daiToken.safeApprove(_trv, 0);
            daiToken.safeIncreaseAllowance(_trv, type(uint256).max);
        }

        emit TlcStrategySet(newTlcStrategy, _trv);

        // The new TRV may have a different debt ceiling. Force an update to the interest rates.
        _updateInterestRates(_debtTokenCache());
    }

    /**
     * @notice Update the interest rate model contract for DAI borrows
     * @param interestRateModel The contract address of the new model
     */
    function setInterestRateModel(
        address interestRateModel
    ) external override onlyElevatedAccess {
        emit InterestRateModelSet(interestRateModel);
        DebtTokenCache memory _cache = _debtTokenCache();

        // Update the cache entry and calculate the new interest rate based off this model.
        debtTokenConfig.interestRateModel = _cache.config.interestRateModel = IInterestRateModel(interestRateModel);
        _updateInterestRates(_cache);
    }

    /**
     * @notice Set the maximum Loan To Value Ratio allowed for DAI borrows before the position is liquidated
     * @param maxLtvRatio The max LTV ratio (18 decimal places)
     */
    function setMaxLtvRatio(
        uint256 maxLtvRatio
    ) external override onlyElevatedAccess {
        if (maxLtvRatio > PRECISION) revert CommonEventsAndErrors.InvalidParam();

        emit MaxLtvRatioSet(maxLtvRatio);
        debtTokenConfig.maxLtvRatio = uint96(maxLtvRatio);
    }

    /**
     * @notice New borrows of DAI may be paused, for example if shutting down.
     */
    function setBorrowPaused(bool isPaused) external override onlyElevatedAccess {
        emit BorrowPausedSet(isPaused);
        debtTokenConfig.borrowsPaused = isPaused;
    }

    /**
     * @notice Elevated access can recover tokens accidentally sent to this contract
     * No user Temple collateral can be taken.
     */
    function recoverToken(
        address token, 
        address to, 
        uint256 amount
    ) external override onlyElevatedAccess {
        // Can't pull any of the user collateral.
        if (token == address(templeToken)) {
            uint256 bal = templeToken.balanceOf(address(this));
            if (amount > (bal - totalCollateral)) revert CommonEventsAndErrors.InvalidAmount(token, amount);
        }

        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }

    /**
     * @notice Update and checkpoint the total debt up until now
     * Then recalculate the interest rate based on the updated utilisation ratio.
     */
    function refreshInterestRates(
    ) external override {
        _updateInterestRates(_debtTokenCache());
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           VIEWS                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice An view of an accounts current and up to date position as of this block
     * @param account The account to get a position for
     */
    function accountPosition(
        address account
    ) external override view returns (
        AccountPosition memory position
    ) {
        AccountData storage _accountData = allAccountsData[account];
        DebtTokenCache memory _cache = _debtTokenCacheRO();

        position.collateral = _accountData.collateral;
        position.currentDebt = _currentAccountDebt(
            _cache, 
            _accountData.debtCheckpoint,
            _accountData.interestAccumulator,
            true
        );

        position.maxBorrow = _maxBorrowLimit(_cache, position.collateral);
        position.healthFactor = _healthFactor(_cache, position.collateral, position.currentDebt);
        position.loanToValueRatio = _loanToValueRatio(_cache, position.collateral, position.currentDebt);
    }

    /**
     * @notice Get the current total DAI debt position across all accounts
     * as of this block.
     */
    function totalDebtPosition() external override view returns (
        TotalDebtPosition memory position
    ) {
        DebtTokenCache memory _cache = _debtTokenCacheRO();
        position.utilizationRatio = _utilizationRatio(_cache);
        position.borrowRate = _cache.interestRate;
        position.totalDebt = _cache.totalDebt;
    }

    /**
     * @notice Compute the liquidity status for a set of accounts.
     * @dev This can be used to verify if accounts can be liquidated or not.
     * @param accounts The accounts to get the status for.
     */
    function computeLiquidity(
        address[] calldata accounts
    ) external override view returns (LiquidationStatus[] memory status) {
        uint256 _numAccounts = accounts.length;
        status = new LiquidationStatus[](_numAccounts);
        DebtTokenCache memory _cache = _debtTokenCacheRO();
        for (uint256 i; i < _numAccounts; ++i) {
            status[i] = _computeLiquidity(
                allAccountsData[accounts[i]], 
                _cache
            );
        }
    }

    /**
     * @notice A view of the last checkpoint of account data (not as of this block)
     */
    function accountData(
        address account
    ) external view override returns (
        AccountData memory
    ) {
        return allAccountsData[account];
    }
    
    /**
     * @notice Configuration and latest data snapshot of the DAI debt token
     */
    function debtTokenDetails() external view returns (
        DebtTokenConfig memory,
        DebtTokenData memory
    ) {
        return (debtTokenConfig, debtTokenData);
    }

    /**
     * @notice A view of the derived/internal cache data.
     */
    function getDebtTokenCache() external view returns (DebtTokenCache memory) {
        return _debtTokenCacheRO();
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          INTERNALS                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @dev An internal struct used to track the latest storage data (and new updates)
     * for a given debt token.
     * This is setup once from storage, and then reads/writes are cheap.
     */
    struct DebtTokenCache {
        /**
         * @notice This debt token's configuration.
         */
        DebtTokenConfig config;

        /**
         * @notice The total amount that has already been borrowed by all accounts.
         * This increases as interest accrues or new borrows. 
         * Decreases on repays or liquidations.
         */
        uint128 totalDebt;

        /**
         * @notice The interest rate as of the last borrow/repay/liquidation.
         * This last rate is used to accrue interest from that last action time
         * until the current block.
         */
        uint96 interestRate;

        /**
         * @notice Internal tracking of the accumulated interest as an index starting from 1.0e27
         * When this accumulator is compunded by the interest rate, the total debt can be calculated as
         * `updatedTotalDebt = prevTotalDebt * latestInterestAccumulator / prevInterestAccumulator
         */
        uint256 interestAccumulator;

        /**
         * @dev The price of this token as of this block. 
         * No external price oracles - only dependant on TPI
         */
        uint256 price;
        
        /**
         * @notice The maximum amount that the TLC Strategy is allowed to borrow from the TRV
         * @dev This puts a ceiling on the total amount allowed to be borrowed
         * by users from TLC. This is used as the denominator in the Utilisation Ratio
         * (the interest rate calcs are dependant on the UR)
         */
        uint256 trvDebtCeiling;
    }
    
    /**
     * @dev Initialize the DebtTokenCache from storage to this block, for a given token.
     */
    function _initDebtTokenCache(DebtTokenCache memory _cache) private view returns (bool dirty) {

        // Copies from storage (once)
        _cache.config = debtTokenConfig;

        _cache.interestAccumulator = debtTokenData.interestAccumulator;
        _cache.totalDebt = debtTokenData.totalDebt;
        _cache.interestRate = debtTokenData.interestRate;

        // Set the debt ceiling and price.
        {
            ITreasuryReservesVault _trv = treasuryReservesVault;
            _cache.trvDebtCeiling = _trv.strategyDebtCeiling(address(tlcStrategy), daiToken);
            _cache.price = _trv.treasuryPriceIndex();
        }
        
        // Only compound if we're on a new block
        uint32 _timeElapsed;
        unchecked {
            _timeElapsed = uint32(block.timestamp) - debtTokenData.interestAccumulatorUpdatedAt;
        }

        if (_timeElapsed > 0) {
            dirty = true;

            // Compound the accumulator
            uint256 newInterestAccumulator = _cache.interestAccumulator.continuouslyCompounded(
                _timeElapsed,
                _cache.interestRate
            );

            // Calculate the latest totalDebt from this
            _cache.totalDebt = mulDiv(
                newInterestAccumulator,
                _cache.totalDebt,
                _cache.interestAccumulator
            ).encodeUInt128();
            _cache.interestAccumulator = newInterestAccumulator;
        }
    }

    /**
     * @dev Setup the DebtTokenCache for a given token
     * Update storage if and only if the timestamp has changed since last time.
     */
    function _debtTokenCache() internal returns (
        DebtTokenCache memory cache
    ) {
        if (_initDebtTokenCache(cache)) {
            debtTokenData.interestAccumulatorUpdatedAt = uint32(block.timestamp);
            debtTokenData.totalDebt = cache.totalDebt;
            debtTokenData.interestAccumulator = cache.interestAccumulator;
        }
    }

    /**
     * @dev Setup the DebtTokenCache for a given token
     * read only -- storage isn't updated.
     */
    function _debtTokenCacheRO() internal view returns (
        DebtTokenCache memory cache
    ) {
        _initDebtTokenCache(cache);
    }

    /**
     * @dev Calculate the borrow interest rate, given the utilization ratio of the token.
     * If the rate has changed, then update storage and emit an event.
     */
    function _updateInterestRates(
        DebtTokenCache memory _cache
    ) internal {
        uint96 newInterestRate = _cache.config.interestRateModel.calculateInterestRate(
            _utilizationRatio(_cache)
        );

        // Update storage if the new rate differs from the old rate.
        if (_cache.interestRate != newInterestRate) {
            emit InterestRateUpdate(newInterestRate);
            debtTokenData.interestRate = _cache.interestRate = newInterestRate;
        }
    }

    /**
     * @dev The implementation of the debt token repayment, used by repay() and repayAll()
     */
    function _repayToken(
        DebtTokenCache memory _cache,
        uint128 _repayAmount,
        address _fromAccount,
        address _onBehalfOf
    ) internal {
        _repayTotalDebt(_cache, _repayAmount);

        emit Repay(_fromAccount, _onBehalfOf, _repayAmount);
        // NB: Liquidity doesn't need to be checked after a repay, as that only improves the health.

        // Pull the stables, and repay the TRV debt on behalf of the strategy.
        {
            daiToken.safeTransferFrom(_fromAccount, address(this), _repayAmount);
            treasuryReservesVault.repay(daiToken, _repayAmount, address(tlcStrategy));
        }
    }

    /**
     * @dev Generate the LiquidationStatus struct with current details 
     * for this account.
     */
    function _computeLiquidity(
        AccountData storage _accountData,
        DebtTokenCache memory _cache
    ) internal view returns (LiquidationStatus memory status) {
        status.collateral = _accountData.collateral;

        status.currentDebt = _currentAccountDebt(
            _cache, 
            _accountData.debtCheckpoint, 
            _accountData.interestAccumulator,
            true // round up for user reported debt
        );

        status.collateralValue = status.collateral * _cache.price / PRECISION;

        status.hasExceededMaxLtv = status.currentDebt > _maxBorrowLimit(
            _cache,
            status.collateral
        );
    }

    /**
     * @dev Check if this account is to be liquidated given the current
     * account, debt token and market conditions.
     * Revert if the account has exceeded the maximum LTV
     */
    function _checkLiquidity(AccountData storage _accountData, DebtTokenCache memory _cache) internal view {
        LiquidationStatus memory _status = _computeLiquidity(
            _accountData,
            _cache
        );
        if (_status.hasExceededMaxLtv) {
            revert ExceededMaxLtv(_status.collateral, _status.collateralValue, _status.currentDebt);
        }
    }

    /**
     * @dev Reduce the total debt in storage by a repayment amount.
     * The sum each users debt may be slightly more than the recorded total debt
     * because users debt is rounded up for dust.
     * The Total debt is floored at 0.
     */
    function _repayTotalDebt(
        DebtTokenCache memory _cache,
        uint128 _repayAmount
    ) internal {
        if (_repayAmount == 0) return;

        unchecked {
            uint128 _newDebt = (_repayAmount > _cache.totalDebt)
                ? 0
                : _cache.totalDebt - _repayAmount;

            debtTokenData.totalDebt = _cache.totalDebt = _newDebt;
        }

        // Update interest rates now the total debt has been updated.
        _updateInterestRates(_cache);
    }

    /**
     * @dev Calculate the Utilization Ratio. 
     * It is only relevant for DAI, where there is a debt ceiling set in the cache.
     * Numerator = The total debt across all users for this token
     * Denominator = The max amount which TLC can borrow from the Treasury Reserves Vault
     */
    function _utilizationRatio(
        DebtTokenCache memory _cache
    ) internal pure returns (uint256) {
        return _cache.trvDebtCeiling == 0
            ? 0
            : mulDiv(_cache.totalDebt, PRECISION, _cache.trvDebtCeiling);
    }
    
    /**
     * @dev mulDiv with an option to round the result up or down to the nearest wei
     */
    function _mulDivRound(uint256 x, uint256 y, uint256 denominator, bool roundUp) internal pure returns (uint256 result) {
        result = mulDiv(x, y, denominator);
        // See OZ Math.sol for the equivalent mulDiv() with rounding.
        if (roundUp && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
    }

    /**
     * @dev Calculate the latest debt for a given account & token.
     * Derived from the prior debt checkpoint, and the interest accumulator.
     */
    function _currentAccountDebt(
        DebtTokenCache memory _cache,
        uint128 _accountDebtCheckpoint,
        uint256 _accountInterestAccumulator,
        bool roundUp
    ) internal pure returns (uint128 result) {
        return (_accountDebtCheckpoint == 0) 
            ? 0
            : _mulDivRound(
                _accountDebtCheckpoint, 
                _cache.interestAccumulator, 
                _accountInterestAccumulator, 
                roundUp
            ).encodeUInt128();
    }

    /**
     * @dev What is the max borrow liit for a given token and 
     * amount of collateral
     */
    function _maxBorrowLimit(
        DebtTokenCache memory _cache,
        uint128 _collateral
    ) internal pure returns (uint128) {
        return mulDiv(
            _collateral * _cache.price,
            _cache.config.maxLtvRatio,
            DOUBLE_PRECISION
        ).encodeUInt128();
    }

    /**
     * @dev What is the health factor, given an amount of 
     * collateral and debt.
     * health = (collateral value / debt value) * max LTV Limit
     */
    function _healthFactor(
        DebtTokenCache memory _cache,
        uint256 _collateral,
        uint256 _debt
    ) internal pure returns (uint256) {
        return _debt == 0
            ? type(uint256).max
            : mulDiv(
                _collateral * _cache.price,
                _cache.config.maxLtvRatio,
                _debt * PRECISION
            );
    }

    /**
     * @dev What is the Loan To Value (LTV), given an amount of 
     * collateral and debt.
     * LTV = debt value / collateral value
     */
    function _loanToValueRatio(
        DebtTokenCache memory _cache,
        uint256 _collateral,
        uint256 _debt
    ) internal pure returns (uint256) {
        return _collateral == 0
            ? type(uint256).max
            : mulDiv(
                _debt,
                DOUBLE_PRECISION,
                _collateral * _cache.price
            );
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 3 of 46 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

File 4 of 46 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 5 of 46 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

File 6 of 46 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 8 of 46 : 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 9 of 46 : 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);
int256 constant uUNIT = 1e18;

File 10 of 46 : 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 11 of 46 : 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 12 of 46 : 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 13 of 46 : 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 The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);

/// @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 14 of 46 : 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 hte 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 15 of 46 : 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 16 of 46 : 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,
    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();

    // 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 this is truncated to zero.
        if (xInt < -59_794705707972522261) {
            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 17 of 46 : 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 18 of 46 : 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 { UD2x18 } from "../ud2x18/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 19 of 46 : 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.
uint256 constant uUNIT = 1e18;
UD2x18 constant UNIT = UD2x18.wrap(1e18);

File 20 of 46 : 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);

File 21 of 46 : 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 22 of 46 : UD60x18.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*

██████╗ ██████╗ ██████╗ ███╗   ███╗ █████╗ ████████╗██╗  ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║  ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║   ██║   ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║   ██║   ██╔══██║
██║     ██║  ██║██████╔╝██║ ╚═╝ ██║██║  ██║   ██║   ██║  ██║
╚═╝     ╚═╝  ╚═╝╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝

██╗   ██╗██████╗  ██████╗  ██████╗ ██╗  ██╗ ██╗ █████╗
██║   ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗
██║   ██║██║  ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝
██║   ██║██║  ██║██╔═══██╗████╔╝██║ ██╔██╗  ██║██╔══██╗
╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝
 ╚═════╝ ╚═════╝  ╚═════╝  ╚═════╝ ╚═╝  ╚═╝ ╚═╝ ╚════╝

*/

import "./ud60x18/Casting.sol";
import "./ud60x18/Constants.sol";
import "./ud60x18/Conversions.sol";
import "./ud60x18/Errors.sol";
import "./ud60x18/Helpers.sol";
import "./ud60x18/Math.sol";
import "./ud60x18/ValueType.sol";

File 23 of 46 : 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 24 of 46 : 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 25 of 46 : Conversions.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x) / uUNIT;
}

/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
    if (x > uMAX_UD60x18 / uUNIT) {
        revert PRBMath_UD60x18_Convert_Overflow(x);
    }
    unchecked {
        result = UD60x18.wrap(x * uUNIT);
    }
}

File 26 of 46 : 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 27 of 46 : 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 28 of 46 : 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));
    }
}

File 29 of 46 : 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 30 of 46 : CommonEventsAndErrors.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (common/CommonEventsAndErrors.sol)

/// @notice A collection of common errors thrown within the Temple contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidParam();
    error InvalidAddress();
    error InvalidAccess();
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Unimplemented();
    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}

File 31 of 46 : SafeCast.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (common/SafeCast.sol)

library SafeCast {
    error Overflow(uint256 amount);

    function encodeUInt128(uint256 amount) internal pure returns (uint128) {
        if (amount > type(uint128).max) {
            revert Overflow(amount);
        }
        return uint128(amount);
    }
}

File 32 of 46 : ITempleElevatedAccess.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/access/ITempleElevatedAccess.sol)

/**
 * @notice Inherit to add Executor and Rescuer roles for DAO elevated access.
 */ 
interface ITempleElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);
    event RescueModeSet(bool indexed value);

    event NewRescuerProposed(address indexed oldRescuer, address indexed oldProposedRescuer, address indexed newProposedRescuer);
    event NewRescuerAccepted(address indexed oldRescuer, address indexed newRescuer);

    event NewExecutorProposed(address indexed oldExecutor, address indexed oldProposedExecutor, address indexed newProposedExecutor);
    event NewExecutorAccepted(address indexed oldExecutor, address indexed newExecutor);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice A set of addresses which are approved to execute emergency operations.
     */ 
    function rescuer() external returns (address);

    /**
     * @notice A set of addresses which are approved to execute normal operations on behalf of the DAO.
     */ 
    function executor() external returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external returns (bool);

    /**
     * @notice Under normal circumstances, rescuers don't have access to admin/operational functions.
     * However when rescue mode is enabled (by rescuers or executors), they claim the access rights.
     */
    function inRescueMode() external returns (bool);
    
    /**
     * @notice Set the contract into or out of rescue mode.
     * Only the rescuers or executors are allowed to set.
     */
    function setRescueMode(bool value) external;

    /**
     * @notice Proposes a new Rescuer.
     * Can only be called by the current rescuer.
     */
    function proposeNewRescuer(address account) external;

    /**
     * @notice Caller accepts the role as new Rescuer.
     * Can only be called by the proposed rescuer
     */
    function acceptRescuer() external;

    /**
     * @notice Proposes a new Executor.
     * Can only be called by the current executor or resucer (if in resuce mode)
     */
    function proposeNewExecutor(address account) external;

    /**
     * @notice Caller accepts the role as new Executor.
     * Can only be called by the proposed executor
     */
    function acceptExecutor() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}

File 33 of 46 : ITempleCircuitBreaker.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/circuitBreaker/ITempleCircuitBreaker.sol)

import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";

/**
 * @title Temple Circuit Breaker
 * 
 * @notice A circuit breaker can perform checks and record state for transactions which have
 * already happened cumulative totals, totals within a rolling period window,
 * sender specific totals, etc.
 */
interface ITempleCircuitBreaker is ITempleElevatedAccess {

    /**
     * @notice Verify the new amount requested for the sender does not breach the
     * cap in this rolling period.
     */
    function preCheck(address onBehalfOf, uint256 amount) external;
}

File 34 of 46 : ITempleCircuitBreakerProxy.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/circuitBreaker/ITempleCircuitBreakerProxy.sol)

import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";
import { ITempleCircuitBreaker } from "contracts/interfaces/v2/circuitBreaker/ITempleCircuitBreaker.sol";

/**
 * @title Temple Circuit Breaker Proxy
 * 
 * @notice Direct circuit breaker requests to the correct underlying implementation,
 * based on a pre-defined bytes32 identifier, and a token.
 */
interface ITempleCircuitBreakerProxy is ITempleElevatedAccess {
    event CircuitBreakerSet(bytes32 indexed identifier, address indexed token, address circuitBreaker);
    event IdentifierForCallerSet(address indexed caller, string identifierString, bytes32 identifier);

    /**
     * @notice A calling contract of the circuit breaker (eg TLC) is mapped to an identifier
     * which means circuit breaker caps can be shared across multiple callers.
     */
    function callerToIdentifier(address) external view returns (bytes32);

    /**
     * @notice The mapping of a (identifier, tokenAddress) tuple to the underlying circuit breaker contract
     */
    function circuitBreakers(
        bytes32 identifier, 
        address token
    ) external view returns (ITempleCircuitBreaker);

    /**
     * @notice Set the identifier for a given caller of the circuit breaker. These identifiers
     * can be shared, such that multiple contracts share the same cap limits for a given token.
     */
    function setIdentifierForCaller(
        address caller, 
        string memory identifierString
    ) external;

    /**
     * @notice Set the address of the circuit breaker for a particular identifier and token
     */
    function setCircuitBreaker(
        bytes32 identifier,
        address token,
        address circuitBreaker
    ) external;

    /**
     * @notice For a given identifier & token, verify the new amount requested for the sender does not breach the
     * cap in this rolling period.
     */
    function preCheck(
        address token,
        address onBehalfOf,
        uint256 amount
    ) external;

    /**
     * @notice The set of all identifiers registered
     */
    function identifiers() external view returns (bytes32[] memory);
}

File 35 of 46 : IInterestRateModel.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/interestRate/IInterestRateModel.sol)

interface IInterestRateModel {
    /**
     * @notice Calculates the current interest rate based on a utilization ratio
     * @param utilizationRatio The utilization ratio scaled to `PRECISION`
     * @return interestRate The interest rate (scaled by PRECISION). 0.05e18 == 5%
     */
    function calculateInterestRate(
        uint256 utilizationRatio
    ) external view returns (uint96 interestRate);
}

File 36 of 46 : ITempleDebtToken.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/ITempleDebtToken.sol)

import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";

interface ITempleDebtToken is IERC20, IERC20Metadata, ITempleElevatedAccess {
    error NonTransferrable();
    error CannotMintOrBurn(address caller);

    event BaseInterestRateSet(uint96 rate);
    event RiskPremiumInterestRateSet(address indexed debtor, uint96 rate);
    event AddedMinter(address indexed account);
    event RemovedMinter(address indexed account);
    event DebtorBalance(address indexed debtor, uint128 principal, uint128 baseInterest, uint128 riskPremiumInterest);

    /**
     * @notice Track the deployed version of this contract. 
     */
    function version() external view returns (string memory);

    /**
     * @notice The current (base rate) interest common for all users. This can be updated by governance
     * @dev 1e18 format, where 0.01e18 = 1%
     */
    function baseRate() external view returns (uint96);

    /**
     * @notice The last checkpoint time of the (base rate) principal and interest checkpoint
     */
    function baseCheckpointTime() external view returns (uint32);

    /**
     * @notice The (base rate) total principal and interest owed across all debtors as of the latest checkpoint
     */
    function baseCheckpoint() external view returns (uint128);

    /**
     * @notice The (base rate) total number of shares allocated out to users for internal book keeping
     */
    function baseShares() external view returns (uint128);

    /**
     * @notice The net amount of principal amount of debt minted across all users.
     */
    function totalPrincipal() external view returns (uint128);

    /**
     * @notice The latest estimate of the (risk premium) interest (no principal) owed.
     * @dev Indicative only. This total is only updated on a per strategy basis when that strategy gets 
     * checkpointed (on borrow/repay rate change).
     * So it is generally always going to be out of date as each strategy will accrue interest independently 
     * on different rates.
     */
    function estimatedTotalRiskPremiumInterest() external view returns (uint128);

    /// @dev byte packed into two slots.
    struct Debtor {
        /// @notice The current principal owed by this debtor
        uint128 principal;

        /// @notice The number of this shares this debtor is allocated of the base interest.
        uint128 baseShares;

        /// @notice The current (risk premium) interest rate specific to this debtor. This can be updated by governance
        /// @dev 1e18 format, where 0.01e18 = 1%
        uint96 rate;

        /// @notice The debtor's (risk premium only) interest (no principal or base interest) owed as of the last checkpoint
        uint128 checkpoint;

        /// @notice The last checkpoint time of this debtor's (risk premium) interest
        /// @dev uint32 => max time of Feb 7 2106
        uint32 checkpointTime;
    }

    /**
     * @notice Per address status of debt
     */
    function debtors(address account) external view returns (
        /// @notice The current principal owed by this debtor
        uint128 principal,

        /// @notice The number of this shares this debtor is allocated of the base interest.
        uint128 baseShares,

        /// @notice The current (risk premium) interest rate specific to this debtor. This can be updated by governance
        /// @dev 1e18 format, where 0.01e18 = 1%
        uint96 rate,

        /// @notice The debtor's (risk premium only) interest (no principal or base interest) owed as of the last checkpoint
        uint128 checkpoint,

        /// @notice The last checkpoint time of this debtor's (risk premium) interest
        uint32 checkpointTime
    );

    /// @notice A set of addresses which are approved to mint/burn
    function minters(address account) external view returns (bool);

    /**
     * @notice Governance can add an address which is able to mint or burn debt
     * positions on behalf of users.
     */
    function addMinter(address account) external;

    /**
     * @notice Governance can remove an address which is able to mint or burn debt
     * positions on behalf of users.
     */
    function removeMinter(address account) external;

    /**
     * @notice Governance can update the continuously compounding (base) interest rate of all debtors, from this block onwards.
     */
    function setBaseInterestRate(uint96 _rate) external;

    /**
     * @notice Governance can update the continuously compounding (risk premium) interest rate for a given debtor, from this block onwards
     */
    function setRiskPremiumInterestRate(address _debtor, uint96 _rate) external;

    /**
     * @notice Approved Minters can add a new debt position on behalf of a user.
     * @param _debtor The address of the debtor who is issued new debt
     * @param _mintAmount The notional amount of debt tokens to issue.
     */
    function mint(address _debtor, uint256 _mintAmount) external;

    /**
     * @notice Approved Minters can burn debt on behalf of a user.
     * @dev Interest is repaid in preference:
     *   1/ Firstly to the higher interest rate of (baseRate, debtor risk premium rate)
     *   2/ Any remaining of the repayment is then paid of the other interest amount.
     *   3/ Finally if there is still some repayment amount unallocated, 
     *      then the principal will be paid down. This is like a new debt is issued for the lower balance,
     *      where interest accrual starts fresh.
     * More debt than the user has cannot be burned - it is capped. The actual amount burned is returned
     * @param _debtor The address of the debtor
     * @param _burnAmount The notional amount of debt tokens to repay.
     */
    function burn(address _debtor, uint256 _burnAmount) external returns (uint256 burnedAmount);

    /**
     * @notice Approved Minters can burn the entire debt on behalf of a user.
     * @param _debtor The address of the debtor
     */
    function burnAll(address _debtor) external returns (uint256 burnedAmount);

    /**
     * @notice Checkpoint the base interest owed by all debtors up to this block.
     */
    function checkpointBaseInterest() external returns (uint256);

    /**
     * @notice Checkpoint a debtor's (risk premium) interest (no principal) owed up to this block.
     */
    function checkpointDebtorInterest(address debtor) external returns (uint256);

    /**
     * @notice Checkpoint multiple accounts (risk premium) interest (no principal) owed up to this block.
     * @dev Provided in case there needs to be block synchronisation on the total debt.
     */
    function checkpointDebtorsInterest(address[] calldata _debtors) external;

    struct DebtOwed {
        uint256 principal;
        uint256 baseInterest;
        uint256 riskPremiumInterest;
    }

    /**
     * @notice The current debt for a given user split out by
     * principal, base interest, risk premium (per debtor) interest
     */
    function currentDebtOf(address _debtor) external view returns (
        DebtOwed memory debtOwed
    );

    /**
     * @notice The current debt for a given set of users split out by
     * principal, base interest, risk premium (per debtor) interest
     */
    function currentDebtsOf(address[] calldata _debtors) external view returns (
        DebtOwed[] memory debtsOwed
    );

    /**
      * @notice The current total principal + total base interest, total (estimate) debtor specific risk premium interest owed by all debtors.
      * @dev Note the (total principal + total base interest) portion is up to date.
      * However the (debtor specific risk premium interest) portion is likely stale.
      * The `estimatedTotalDebtorInterest` is only updated when each debtor checkpoints, so it's going to be out of date.
      * For more up to date current totals, off-chain aggregation of balanceOf() will be required - eg via subgraph.
      */
    function currentTotalDebt() external view returns (
        DebtOwed memory debtOwed
    );

    /**
     * @notice Convert a (base interest) debt amount into proportional amount of shares
     */
    function baseDebtToShares(uint128 debt) external view returns (uint128);

    /**
     * @notice Convert a number of (base interest) shares into proportional amount of debt
     */
    function baseSharesToDebt(uint128 shares) external view returns (uint128);
}

File 37 of 46 : ITreasuryPriceIndexOracle.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/ITreasuryPriceIndexOracle.sol)

import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";

/**
 * @title Treasury Price Index Oracle
 * @notice The custom oracle (not dependant on external markets/AMMs/dependencies) to give the
 * Treasury Price Index, representing the target Treasury Value per token.
 * This rate is updated manually with elevated permissions. The new TPI doesn't take effect until after a cooldown.
 */
interface ITreasuryPriceIndexOracle is ITempleElevatedAccess {
    event TreasuryPriceIndexSet(uint96 oldTpi, uint96 newTpi);
    event TpiCooldownSet(uint32 cooldownSecs);
    event MaxTreasuryPriceIndexDeltaSet(uint256 maxDelta);

    error BreachedMaxTpiDelta(uint96 oldTpi, uint96 newTpi, uint256 maxDelta);

    /**
     * @notice The current Treasury Price Index (TPI) value
     * @dev If the TPI has just been updated, the old TPI will be used until `cooldownSecs` has elapsed
     */
    function treasuryPriceIndex() external view returns (uint96);

    /**
     * @notice The maximum allowed TPI change on any single `setTreasuryPriceIndex()`, in absolute terms.
     * @dev Used as a bound to avoid unintended/fat fingering when updating TPI
     */
    function maxTreasuryPriceIndexDelta() external view returns (uint256);

    /**
     * @notice The current internal TPI data along with when it was last reset, and the prior value
     */
    function tpiData() external view returns (
        uint96 currentTpi,
        uint96 previousTpi,
        uint32 lastUpdatedAt,
        uint32 cooldownSecs
    );

    /**
     * @notice Set the Treasury Price Index (TPI)
     */
    function setTreasuryPriceIndex(uint96 value) external;

    /**
     * @notice Set the number of seconds to elapse before a new TPI will take effect.
     */
    function setTpiCooldown(uint32 cooldownSecs) external;

    /**
     * @notice Set the maximum allowed TPI change on any single `setTreasuryPriceIndex()`, in absolute terms.
     * @dev 18 decimal places, 0.20e18 == $0.20
     */
    function setMaxTreasuryPriceIndexDelta(uint256 maxDelta) external;

    /**
     * @notice The decimal precision of Temple Price Index (TPI)
     * @dev 18 decimals, so 1.02e18 == $1.02
     */
    // solhint-disable-next-line func-name-mixedcase
    function TPI_DECIMALS() external view returns (uint256);
}

File 38 of 46 : ITreasuryReservesVault.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/ITreasuryReservesVault.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ITempleDebtToken } from "contracts/interfaces/v2/ITempleDebtToken.sol";
import { ITempleStrategy } from "contracts/interfaces/v2/strategies/ITempleStrategy.sol";
import { ITempleBaseStrategy } from "contracts/interfaces/v2/strategies/ITempleBaseStrategy.sol";
import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";
import { ITreasuryPriceIndexOracle } from "contracts/interfaces/v2/ITreasuryPriceIndexOracle.sol";

/**
 * @title Treasury Reserves Vault (TRV)
 *
 * @notice Temple has various strategies which utilise the treasury funds to generate 
 * gains for token holders.
 * 
 * The maximum amount of funds allocated to each strategy is determined by governance, 
 * and then each strategy can borrow/repay as required (up to the cap).
 * 
 * When strategies borrow funds, they are issued `dToken`, an accruing debt token representing
 * the debt to the temple treasury. This is used to compare strategies performance, where
 * we can determine an equity value (assets - debt).
 *
 *    Strategies can borrow different types of tokens from the TRV, and are minted equivalent internal debt tokens eg:
 *      DAI => minted dUSD
 *      TEMPLE => minted dTEMPLE
 *      ETH => minted dETH
 *   
 *   Each of the dTokens are compounding at different risk free rates, eg:
 *      dUSD: At DAIs Savings Rate (DSR)
 *      dTEMPLE: 0% interest (no opportunity cost)
 *      dETH: ~avg LST rate
 *   
 *   And so each token which can be borrowed has separate config on how to pull/deposit idle funds.
 *   For example, this may be:
 *      DAI => DSR base strategy
 *      TEMPLE => direct Temple mint/burn 
 *      ETH => just hold in a vault
 */
interface ITreasuryReservesVault is ITempleElevatedAccess {
    event GlobalPausedSet(bool borrow, bool repay);
    event StrategyPausedSet(address indexed strategy, bool borrow, bool repay);

    event StrategyAdded(address indexed strategy, int256 underperformingEquityThreshold);
    event StrategyRemoved(address indexed strategy);
    event DebtCeilingUpdated(address indexed strategy, address indexed token, uint256 oldDebtCeiling, uint256 newDebtCeiling);
    event UnderperformingEquityThresholdUpdated(address indexed strategy, int256 oldThreshold, int256 newThreshold);
    event StrategyIsShuttingDownSet(address indexed strategy, bool isShuttingDown);
    event StrategyShutdownCreditAndDebt(address indexed strategy, address indexed token, uint256 outstandingCredit, uint256 outstandingDebt);
    event StrategyCreditAndDebtBalance(address indexed strategy, address indexed token, uint256 credit, uint256 debt);

    event BorrowTokenSet(address indexed token, address baseStrategy, uint256 baseStrategyWithdrawalBuffer, uint256 baseStrategyDepositThreshold, address dToken);
    event BorrowTokenRemoved(address indexed token);

    event Borrow(address indexed strategy, address indexed token, address indexed recipient, uint256 amount);
    event Repay(address indexed strategy, address indexed token, address indexed from, uint256 amount);

    event TpiOracleSet(address indexed tpiOracle);

    error BorrowTokenNotEnabled();
    error StrategyNotEnabled();

    error AlreadyEnabled();
    error BorrowPaused();
    error RepaysPaused();
    error StrategyIsShutdown();
    error DebtCeilingBreached(uint256 available, uint256 borrowAmount);
    error NotShuttingDown();

    struct BorrowTokenConfig {
        /**
         * @notice The base strategy used to hold idle treasury funds
         */
        ITempleBaseStrategy baseStrategy;

        /**
         * @notice A buffer of tokens are maintained in the TRV such that it doesn't have to churn through small base strategy
         * deposits/withdrawals. 
         * On a borrow if tokens need to be pulled from the base strategy, more than the requested amount is withdrawn such that
         * this extra buffer of tokens ends up in the TRV
         */
        uint256 baseStrategyWithdrawalBuffer;

        /**
         * @notice When repayments are made to the TRV, tokens are only deposited into the base strategy once this threshold is met.
         * The amount deposited will ensure that the `baseStrategyWithdrawalBuffer` amount remains in the TRV
         */
        uint256 baseStrategyDepositThreshold;

        /**
         * @notice The address of the internal debt token for this borrow token
         * @dev eg Temple => dTEMPLE, DAI => dUSD
         */
        ITempleDebtToken dToken;
    }
    
    struct StrategyConfig {
        /**
         * @notice Pause borrows
         */
        bool borrowPaused;

        /**
         * @notice Pause repayments
         */
        bool repaysPaused;

        /**
         * @notice Governance nominates this strategy to be shutdown.
         * The strategy executor then needs to unwind (may be manual) 
         * and the strategy needs to then call shutdown() when ready.
         */
        bool isShuttingDown;

        /**
         * @notice Each strategy will have a different threshold of expected performance.
         * This underperforming threshold is used for reporting to determine if the strategy is underperforming.
         */
        int256 underperformingEquityThreshold;

        /**
         * @notice The strategy can borrow up to this limit of accrued debt for each token.
         * The `dToken` is minted on any borrows 1:1 (which then accrues interest)
         * When a strategy repays, the `dToken` is burned 1:1
         */
        mapping(IERC20 => uint256) debtCeiling;

        /**
         * @notice The tokens that this strategy is allowed to borrow from TRV
         * @dev This must be one of the configured Borrow Tokens
         */
        mapping(IERC20 => bool) enabledBorrowTokens;
    }

    /**
     * @notice True if all borrows are paused for all strategies.
     */
    function globalBorrowPaused() external view returns (bool);

    /**
     * @notice True if all repayments are paused for all strategies.
     */
    function globalRepaysPaused() external view returns (bool);

    /**
     * @notice The configuration for a given strategy
     */
    function strategies(address strategy) external view returns (
        bool borrowPaused,
        bool repaysPaused,
        bool isShuttingDown,
        int256 underperformingEquityThreshold
    );

    /**
     * @notice The list of all strategies currently added to the TRV
     */
    function strategiesList() external view returns (address[] memory);

    /**
     * @notice The configuration for a given token which can be borrowed by strategies
     */
    function borrowTokens(IERC20 token) external view returns (
        ITempleBaseStrategy baseStrategy,
        uint256 baseStrategyWithdrawalBuffer,
        uint256 baseStrategyDepositThreshold,
        ITempleDebtToken dToken
    );

    /**
     * @notice The list of all tokens which can be borrowed by the TRV
     */
    function borrowTokensList() external view returns (address[] memory);

    /**
     * @notice When strategies repay a token which covers more than their dToken debt for the token
     * They receive credits. When they next need to borrow tokens this credit is used prior to
     * issuing more dTokens
     */
    function strategyTokenCredits(address strategy, IERC20 token) external view returns (uint256);

    /**
     * @notice The Treasury Price Index Oracle
     */
    function tpiOracle() external view returns (ITreasuryPriceIndexOracle);

    /**
     * @notice Set the Treasury Price Index (TPI) Oracle
     */
    function setTpiOracle(address tpiOracleAddress) external;

    /**
     * @notice The Treasury Price Index - the target price of the Treasury, in `stableToken` terms.
     */
    function treasuryPriceIndex() external view returns (uint96);

    /**
     * @notice API version to help with future integrations/migrations
     */
    function apiVersion() external pure returns (string memory);

    /**
     * @notice Set the borrow token configuration. 
     * @dev This can either add a new token or update an existing token.
     */
    function setBorrowToken(
        IERC20 token, 
        address baseStrategy,
        uint256 baseStrategyWithdrawalBuffer,
        uint256 baseStrategyDepositThreshold,
        address dToken
    ) external;

    /**
     * @notice Enable and/or disable tokens which a strategy can borrow from the (configured) TRV borrow tokens
     */
    function updateStrategyEnabledBorrowTokens(
        address strategy, 
        IERC20[] calldata enableBorrowTokens, 
        IERC20[] calldata disableBorrowTokens
    ) external;

    /**
     * @notice Remove the borrow token configuration. 
     */
    function removeBorrowToken(
        IERC20 token
    ) external;

    /**
     * @notice A helper to collate information about a given strategy for reporting purposes.
     * @dev Note the current assets may not be 100% up to date, as some strategies may need to checkpoint based
     * on the underlying strategy protocols (eg DSR for DAI would need to checkpoint to get the latest valuation).
     */
    function strategyDetails(address strategy) external view returns (
        string memory name,
        string memory version,
        bool borrowPaused,
        bool repaysPaused,
        bool isShuttingDown,
        int256 underperformingEquityThreshold,
        ITempleStrategy.AssetBalance[] memory debtCeiling
    );

    /**
     * @notice A strategy's current asset balances, any manual adjustments and the current debt
     * of the strategy.
     * 
     * This will be used to report equity performance: `sum($assetValue +- $manualAdj) - debt`
     * The conversion of each asset price into the stable token (eg DAI) will be done off-chain
     * along with formulating the union of asset balances and manual adjustments
     */
    function strategyBalanceSheet(address strategyAddr) external view returns (
        ITempleStrategy.AssetBalance[] memory assetBalances,
        ITempleStrategy.AssetBalanceDelta[] memory manualAdjustments, 
        ITempleStrategy.AssetBalance[] memory dTokenBalances,
        ITempleStrategy.AssetBalance[] memory dTokenCreditBalances
    );

    /**
     * @notice The current max debt ceiling that a strategy is allowed to borrow up to.
     */
    function strategyDebtCeiling(address strategy, IERC20 token) external view returns (uint256);
    
    /**
     * @notice Whether a token is enabled to be borrowed for a given strategy
     */
    function strategyEnabledBorrowTokens(address strategy, IERC20 token) external view returns (bool);

    /**
     * @notice The total available stables, both as a balance in this contract and
     * any available to withdraw from the baseStrategy
     */
    function totalAvailable(IERC20 token) external view returns (uint256);

    /**
     * @notice The amount remaining that a strategy can borrow for a given token
     * taking into account: the approved debt ceiling, current dToken debt, and any credits
     * @dev available == min(ceiling - debt + credit, 0)
     */
    function availableForStrategyToBorrow(
        address strategy, 
        IERC20 token
    ) external view returns (uint256);

    /**
     * @notice Pause all strategy borrow and repays
     */
    function setGlobalPaused(bool borrow, bool repays) external;

    /**
     * @notice Set whether borrows and repayments are paused for a given strategy.
     */
    function setStrategyPaused(address strategy, bool borrow, bool repays) external;

    /**
     * @notice Register a new strategy which can borrow tokens from Treasury Reserves
     */
    function addStrategy(
        address strategy, 
        int256 underperformingEquityThreshold,
        ITempleStrategy.AssetBalance[] calldata debtCeiling
    ) external;

    /**
     * @notice Update the debt ceiling for a given strategy
     */
    function setStrategyDebtCeiling(address strategy, IERC20 token, uint256 newDebtCeiling) external;

    /**
     * @notice Update the underperforming equity threshold.
     */
    function setStrategyUnderperformingThreshold(address strategy, int256 underperformingEquityThreshold) external;

    /**
     * @notice The first step in a two-phase shutdown. Executor first sets whether a strategy is slated for shutdown.
     * The strategy then needs to call shutdown as a separate call once ready.
     */
    function setStrategyIsShuttingDown(address strategy, bool isShuttingDown) external;

    /**
     * @notice The second step in a two-phase shutdown. A strategy (automated) or executor (manual) calls
     * to effect the shutdown. isShuttingDown must be true for the strategy first.
     * The strategy executor is responsible for unwinding all it's positions first and repaying the debt to the TRV.
     * All outstanding dToken debt is burned, leaving a net gain/loss of equity for the shutdown strategy.
     */
    function shutdown(address strategy) external;   

    /**
     * @notice A strategy calls to request more funding.
     * @dev This will revert if the strategy requests more stables than it's able to borrow.
     * `dToken` will be minted 1:1 for the amount of stables borrowed
     */
    function borrow(IERC20 token, uint256 borrowAmount, address recipient) external;

    /**
     * @notice A strategy calls to request the most funding it can.
     * @dev This will revert if the strategy requests more stables than it's able to borrow.
     * `dToken` will be minted 1:1 for the amount of stables borrowed
     */
    function borrowMax(IERC20 token, address recipient) external returns (uint256);

    /**
     * @notice A strategy calls to paydown it's debt
     * This will pull the stables, and will burn the equivalent amount of dToken from the strategy.
     */
    function repay(IERC20 token, uint256 repayAmount, address strategy) external;

    /**
     * @notice A strategy calls to paydown all of it's debt
     * This will pull the stables for the entire dToken balance of the strategy, and burn the dToken.
     */
    function repayAll(IERC20 token, address strategy) external returns (uint256 amountRepaid);
}

File 39 of 46 : ITempleBaseStrategy.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/strategies/ITempleBaseStrategy.sol)

import { ITempleStrategy } from "contracts/interfaces/v2/strategies/ITempleStrategy.sol";

/**
 * @title Temple Base Strategy
 * @notice A special Temple Strategy which is eligable to transiently apply capital
 * into a very safe yield bearing protocol (eg DAI Savings Rate).
 * 
 * The Treasury Reserves Vault will have permission to pull back funds from this strategy
 * at any time, for example when another strategy wants to borrow funds.
 */
interface ITempleBaseStrategy {
    /**
     * @notice The latest checkpoint of each asset balance this strategy holds.
     *
     * @dev The asset value may be stale at any point in time, depending on the strategy. 
     * It may optionally implement `checkpointAssetBalances()` in order to update those balances.
     */
    function latestAssetBalances() external view returns (ITempleStrategy.AssetBalance[] memory assetBalances);

    /**
     * @notice The same as `borrowMax()` but for a pre-determined amount to borrow,
     * such that something upstream/off-chain can determine the amount.
     */
    function borrowAndDeposit(uint256 amount) external;

    /**
     * @notice When the TRV has a surplus of funds (over the configured buffer threshold)
     * it will transfer tokens to the base strategy, and call this function to apply
     * the new captial.
     */
    function trvDeposit(uint256 amount) external;

    /**
     * @notice The TRV is able to withdraw on demand in order to fund other strategies which 
     * wish to borrow from the TRV.
     * @dev It may withdraw less than requested if there isn't enough balance in the DSR.
     */
    function trvWithdraw(uint256 requestedAmount) external returns (uint256 amountWithdrawn);

}

File 40 of 46 : ITempleStrategy.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/strategies/ITempleStrategy.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ITreasuryReservesVault } from "contracts/interfaces/v2/ITreasuryReservesVault.sol";
import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";

/**
 * @title Temple Strategy
 * @notice The Temple Strategy is responsible for borrowing funds from the Treasury Reserve Vault
 * and generating positive equity from that capital.
 * 
 * When it borrows funds it is issued systematic debt (`dToken`) which accrues interest at a common base rate
 * plus a risk premium rate specific to this strategy, agreed and set by governance.
 *
 * The strategy reports it's assets (total available funds in investments)
 * in order to report the equity of the strategy -- ie a comparable performance metric across all strategy's.
 *
 * The Strategy Executor role is responsible for applying the capital within the strategy, and can borrow funds from
 * the TRV up to a cap (set by governance). Similarly the Executor is responsible for operations - borrow/repay/liquidate/etc.
 *
 * The strategy can be shutdown - first by Executors giving the go-ahead by setting it to `isShuttingDown` in the TRV
 * and then the Executor can either:
 *   a/ Graceful shutdown, where any liquidation can happen automatically
 *   b/ Force shutdown, where the Executor needs to handle any liquidations manually and send funds back to Treasury first.
 */
interface ITempleStrategy is ITempleElevatedAccess {
    struct AssetBalance {
        address asset;
        uint256 balance;
    }

    struct AssetBalanceDelta {
        address asset;
        int256 delta;
    }

    event TreasuryReservesVaultSet(address indexed trv);
    event Shutdown();
    event AssetBalancesCheckpoint(AssetBalance[] assetBalances);
    event ManualAdjustmentsSet(AssetBalanceDelta[] adjustments);
    event TokenAllowanceSet(address token, address spender, uint256 amount);
    
    error InvalidVersion(string expected, string actual);
    error OnlyTreasuryReserveVault(address caller);

    /**
     * @notice API version to help with future integrations/migrations
     */
    function apiVersion() external view returns (string memory);

    /**
     * @notice A human readable name of the strategy
     */
    function strategyName() external view returns (string memory);

    /**
     * @notice The version of this particular strategy
     */
    function strategyVersion() external view returns (string memory);

    /**
     * @notice The address of the treasury reserves vault.
     */
    function treasuryReservesVault() external view returns (ITreasuryReservesVault);

    /**
     * @notice Executors can set the address of the treasury reserves vault.
     */
    function setTreasuryReservesVault(address _trv) external;

    /**
     * @notice The Strategy Executor may set manual adjustments to asset balances
     * if they cannot be reported automatically - eg a staked position with no receipt token.
     */
    function setManualAdjustments(AssetBalanceDelta[] calldata adjustments) external;

    /**
     * @notice Get the set of manual asset balance deltas, set by the Strategy Executor.
     */
    function manualAdjustments() external view returns (AssetBalanceDelta[] memory adjustments);

    /**
     * @notice The latest checkpoint of each asset balance this strategy holds.
     *
     * @dev The asset value may be stale at any point in time, depending on the strategy. 
     * It may optionally implement `checkpointAssetBalances()` in order to update those balances.
     */
    function latestAssetBalances() external view returns (AssetBalance[] memory assetBalances);

    /**
     * @notice By default, we assume there is no checkpoint required for a strategy
     * In which case it would be identical to just calling `latestAssetBalances()`
     *
     * A strategy can override this if on-chain functions are required to run to force balance
     * updates first - eg checkpoint DSR
     */
    function checkpointAssetBalances() external returns (AssetBalance[] memory assetBalances);

    /**
     * @notice populate data required for shutdown - for example quote data.
     * This may/may not be required in order to do a shutdown. For example to avoid frontrunning/MEV
     * quotes to exit an LP position may need to be obtained off-chain prior to the actual shutdown.
     * Each strategy can abi encode params that it requires.
     * @dev Intentionally not a view - as some quotes require a non-view (eg Balancer)
     * The intention is for clients to call as 'static', like a view
     */
    function populateShutdownData(bytes calldata populateParamsData) external returns (bytes memory shutdownParamsData);

    /**
     * @notice The strategy executor can shutdown this strategy, only after Executors have
     * marked the strategy as `isShuttingDown` in the TRV.
     * This should handle all liquidations and send all funds back to the TRV, and will then call `TRV.shutdown()`
     * to apply the shutdown.
     * @dev Each strategy may require a different set of params to do the shutdown. It can abi encode/decode
     * that data off chain, or by first calling populateShutdownData()
     */
    function automatedShutdown(bytes calldata shutdownParamsData) external;

    /**
     * @notice Executors can recover any token from the strategy.
     */
    function recoverToken(address token, address to, uint256 amount) external;

    /**
     * @notice Executors can set the allowance of any token spend from the strategy
     */
    function setTokenAllowance(IERC20 token, address spender, uint256 amount) external;

    /**
     * @notice A hook which is called by the Treasury Reserves Vault when the debt ceiling
     * for this strategy is updated
     * @dev by default it's a no-op unless the strategy implements `_debtCeilingUpdated()`
     */
    function debtCeilingUpdated(IERC20 token, uint256 newDebtCeiling) external;
}

File 41 of 46 : ITlcStrategy.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/strategies/ITlcStrategy.sol)

import { ITempleStrategy } from "contracts/interfaces/v2/strategies/ITempleStrategy.sol";

/**
 * @title Temple Line of Credit Strategy
 * @notice A simple wrapper strategy over TLC, where
 * the assets is the current total user debt.
 */
interface ITlcStrategy is ITempleStrategy {
    /**
     * @notice TLC (only) will call on this to fund user borrows of DAI
     */
    function fundFromTrv(uint256 amount, address recipient) external;
}

File 42 of 46 : ITempleLineOfCredit.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/templeLineOfCredit/ITempleLineOfCredit.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { ITlcEventsAndErrors } from "contracts/interfaces/v2/templeLineOfCredit/ITlcEventsAndErrors.sol";
import { ITlcDataTypes } from "contracts/interfaces/v2/templeLineOfCredit/ITlcDataTypes.sol";
import { ITreasuryReservesVault } from "contracts/interfaces/v2/ITreasuryReservesVault.sol";
import { ITlcStrategy } from "contracts/interfaces/v2/strategies/ITlcStrategy.sol";
import { ITempleCircuitBreakerProxy } from "contracts/interfaces/v2/circuitBreaker/ITempleCircuitBreakerProxy.sol";

/**
 * @title Temple Line of Credit (TLC)
 * @notice Users supply Temple as collateral, and can then borrow DAI.
 * 
 * Temple is valued at the Temple Treasury Price Index (TPI)
 * User debt increases at a continuously compounding rate.
 * Liquidations occur when users LTV exceeds the maximum allowed.
 */
interface ITempleLineOfCredit is ITlcDataTypes, ITlcEventsAndErrors {
    /**
     * @notice Deposit Temple as collateral
     * @param collateralAmount The amount to deposit
     * @param onBehalfOf An account can add collateral on behalf of another address.
     */
    function addCollateral(uint128 collateralAmount, address onBehalfOf) external;

    /**
     * @notice Remove Temple collateral. (active borrow positions are not allowed to go above the max LTV)
     * @param amount The amount of collateral to remove
     * @param recipient Send the Temple collateral to a specified recipient address.
     */
    function removeCollateral(uint128 amount, address recipient) external;

    /**
     * @notice Borrow DAI (not allowed to borrow over the max LTV)
     * @param amount The amount to borrow
     * @param recipient Send the borrowed token to a specified recipient address.
     */
    function borrow(uint128 amount, address recipient) external;

    /**
     * @notice An account repays some of its DAI debt
     * @param repayAmount The amount to repay. Cannot be more than the current debt.
     * @param onBehalfOf Another address can repay the debt on behalf of someone else
     */
    function repay(uint128 repayAmount, address onBehalfOf) external;
    
    /**
     * @notice An account repays all of its DAI debt
     * @dev The amount of debt is calculated as of this block.
     * @param onBehalfOf Another address can repay the debt on behalf of someone else
     */
    function repayAll(address onBehalfOf) external;

    /**
     * @notice Liquidate one or more accounts which have exceeded the 
     * maximum allowed LTV.
     * The Temple collateral is seized, and the accounts debt wiped.
     * @dev If one of the accounts in the batch hasn't exceeded the max LTV
     * then no action is performed for that account.
     */
    function batchLiquidate(
        address[] calldata accounts
    ) external returns (
        uint128 totalCollateralClaimed,
        uint128 totalDaiDebtWiped
    );

    /**
     * @notice New borrows of DAI may be paused in an emergency to protect user funds
     */
    function setBorrowPaused(bool isPaused) external;

    /**
     * @notice Liquidations may be paused in order for users to recover/repay debt after emergency
     * actions
     */
    function setLiquidationsPaused(bool isPaused) external;

    /**
     * @notice Set the minimum amount of Temple which must be borrowed on each call.
     */
    function setMinBorrowAmount(uint128 amount) external;

    /**
     * @notice Update the TLC Strategy contract, and Treasury Reserves Vault (TRV)
     * @dev The TRV is granted access to spend DAI, in order to repay debt.
     */
    function setTlcStrategy(address _tlcStrategy) external;
    
    /**
     * @notice Update the interest rate model contract for DAI borrows
     * @param interestRateModel The contract address of the new model
     */
    function setInterestRateModel(address interestRateModel) external;
    
    /**
     * @notice Set the maximum Loan To Value Ratio allowed for DAI borrows before the position is liquidated
     * @param maxLtvRatio The max LTV ratio (18 decimal places)
     */
    function setMaxLtvRatio(uint256 maxLtvRatio) external;
    
    /**
     * @notice Elevated access can recover tokens accidentally sent to this contract
     * No user Temple collateral can be taken.
     */
    function recoverToken(address token, address to, uint256 amount) external;

    /**
     * @notice Update and checkpoint the total debt up until now
     * Then recalculate the interest rate based on the updated utilisation ratio.
     */
    function refreshInterestRates() external;
    
    /**
     * @notice The collateral token supplied by users/accounts
     */
    function templeToken() external view returns (IERC20);
    
    /**
     * @notice DAI token -- the debt token which can be borrowed
     */
    function daiToken() external view returns (IERC20);

    /**
     * @notice The Treasury Reserve Vault (TRV) which funds the DAI borrows to users/accounts.
     * - When users borrow, the DAI is pulled from the TRV
     *      (via the TlcStrategy, increasing the dUSD debt)
     * - When users repay, the DAI is repaid to the TRV 
     *      (reducing the dUSD debt of the TlcStrategy)
     * - When there is a liquidation, the seized Temple collateral is paid to the TRV
     *      (reducing the dTEMPLE debt of the TlcStrategy)
     */
    function treasuryReservesVault() external view returns (ITreasuryReservesVault);

    /**
     * @notice The Strategy contract managing the TRV borrows and equity positions of TLC.
     */
    function tlcStrategy() external view returns (ITlcStrategy);

    /**
     * @notice A record of the total amount of collateral deposited by users/accounts.
     */
    function totalCollateral() external view returns (uint256);

    /**
     * @notice Liquidations may be paused in order for users to recover/repay debt after emergency
     * actions
     */
    function liquidationsPaused() external view returns (bool);

    /**
     * @notice The minimum borrow amount per transaction
     * @dev It costs gas to liquidate users, so we don't want dust amounts.
     */
    function minBorrowAmount() external view returns (uint128);

    /**
     * @notice An view of an accounts current and up to date position as of this block
     * @param account The account to get a position for
     */
    function accountPosition(
        address account
    ) external view returns (
        AccountPosition memory position
    );

    /**
     * @notice Get the current total DAI debt position across all accounts
     * as of this block.
     */
    function totalDebtPosition() external view returns (
        TotalDebtPosition memory daiPosition
    );

    /**
     * @notice Compute the liquidity status for a set of accounts.
     * @dev This can be used to verify if accounts can be liquidated or not.
     * @param accounts The accounts to get the status for.
     */
    function computeLiquidity(
        address[] calldata accounts
    ) external view returns (LiquidationStatus[] memory status);

    /**
     * @notice A view of the last checkpoint of account data (not as of this block)
     */
    function accountData(
        address account
    ) external view returns (AccountData memory data);

    /**
     * @notice Configuration and latest data snapshot of the DAI debt token
     */
    function debtTokenDetails() external view returns (
        DebtTokenConfig memory config, 
        DebtTokenData memory data
    );

    /**
     * @notice New borrows and collateral withdrawals are checked against a circuit breaker
     * to ensure no more than a cap is withdrawn in a given period
     */
    function circuitBreakerProxy() external view returns (ITempleCircuitBreakerProxy);
}

File 43 of 46 : ITlcDataTypes.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/templeLineOfCredit/ITlcDataTypes.sol)

import { IInterestRateModel } from "contracts/interfaces/v2/interestRate/IInterestRateModel.sol";

interface ITlcDataTypes {
    /// @notice Configuration of the DAI Debt Token
    struct DebtTokenConfig {
        /// @notice Maximum Loan To Value (LTV) ratio an account can have before being liquidated
        uint96 maxLtvRatio;

        /// @notice The borrow interest rate model contract
        IInterestRateModel interestRateModel;

        /// @notice Pause new borrows
        bool borrowsPaused;
    }

    /// @notice The latest checkpoint of Debt Token data
    /// @dev Packed slot: 32 + 128 + 96 = 256
    struct DebtTokenData {
        /// @notice The last time the debt was updated for this token
        uint32 interestAccumulatorUpdatedAt;

        /// @notice Total amount of debt which has been borrowed across all users
        uint128 totalDebt;

        /// @notice The last calculated borrow interest rate
        uint96 interestRate;

        /// @notice The accumulator index used to track the compounding of debt.
        uint256 interestAccumulator;
    }

    /// @notice The record of an account's collateral, requests
    /// and DAI debt data
    /// @dev Packed slots: (128 + 128), (128 + 128), (128 +  64 + 64)
    struct AccountData {
        /// @notice The amount of collateral the account has posted
        uint128 collateral;
        
        /** 
         * @notice A checkpoint of user debt, updated after a borrow/repay/liquidation
         * @dev Debt as of now =  (
         *    `account.debtCheckpoint` *
         *    `debtTokenData.interestAccumulator` / 
         *    `account.interestAccumulator`
         * )
         */
        uint128 debtCheckpoint;

        /// @notice The account's last interest accumulator checkpoint
        uint256 interestAccumulator;
    }

    /// @notice The status for whether an account can be liquidated or not
    struct LiquidationStatus {
        /// @notice True if the DAI borrow position has exceeded the max LTV
        /// in which case it can be liquidated
        bool hasExceededMaxLtv;

        /// @notice The amount of collateral which has been provided by the user
        uint128 collateral;

        /// @notice The value of collateral (amount * TPI) which has been provided by the user
        uint256 collateralValue;

        /// @notice The amount of DAI debt as of this block
        uint128 currentDebt;
    }
    
    /// @notice An account's collateral and DAI debt position
    struct AccountPosition {
        /// @notice The amount (not the value) of collateral which has been provided by the user
        uint128 collateral;

        /// @notice The amount of DAI debt as of this block
        uint128 currentDebt;

        /// @notice The maximum amount this account can borrow given the collateral posted.
        /// @dev Note if this max is actually borrowed then it will immediately be liquidated as 1 block
        /// of interest will tip it over the max allowed LTV
        uint128 maxBorrow;

        /// @notice The health factor of this accounts position. Anything less than 1 can be liquidated.
        uint256 healthFactor;

        /// @notice The current LTV ratio of this account
        uint256 loanToValueRatio;
    }

    /// @notice The total DAI debt position metrics across all accounts
    struct TotalDebtPosition {
        /// @notice The utilization rate as of the last checkpoint
        uint256 utilizationRatio;

        // @notice The borrow interest rate as of the last checkpoint
        uint256 borrowRate;

        // @notice The total debt across all accounts as of this block
        uint256 totalDebt;
    }
}

File 44 of 46 : ITlcEventsAndErrors.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/templeLineOfCredit/ITlcEventsAndErrors.sol)

interface ITlcEventsAndErrors {
    error ExceededMaxLtv(uint256 collateralAmount, uint256 collateralValue, uint256 currentDaiDebt);
    error ExceededBorrowedAmount(uint256 totalDebtAmount, uint256 repayAmount);
    error InsufficientAmount(uint256 required, uint256 provided);
    error Paused();

    event TlcStrategySet(address indexed strategy, address indexed treasuryReservesVault);
    event InterestRateModelSet(address indexed interestRateModel);
    event MaxLtvRatioSet(uint256 maxLtvRatio);
    event CollateralAdded(address indexed fundedBy, address indexed onBehalfOf, uint128 collateralAmount);
    event CollateralRemoved(address indexed account, address indexed recipient, uint128 collateralAmount);
    event Borrow(address indexed account, address indexed recipient, uint128 amount);
    event Repay(address indexed fundedBy, address indexed onBehalfOf, uint128 repayAmount);
    event Liquidated(address indexed account, uint128 collateralSeized, uint256 collateralValue, uint128 daiDebtWiped);
    event InterestRateUpdate(uint96 newInterestRate);
    event BorrowPausedSet(bool isPaused);
    event LiquidationsPausedSet(bool isPaused);
    event MinBorrowAmountSet(uint128 amount);
}

File 45 of 46 : TempleElevatedAccess.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/access/TempleElevatedAccess.sol)

import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/common/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Executor and Rescuer roles for DAO elevated access.
 */ 
abstract contract TempleElevatedAccess is ITempleElevatedAccess {
    /**
     * @notice The address which is approved to execute emergency operations.
     */ 
    address public override rescuer;

    /**
     * @notice The address which is approved to execute normal operations on behalf of the DAO.
     */ 
    address public override executor;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /**
     * @notice Under normal circumstances, rescuers don't have access to admin/operational functions.
     * However when rescue mode is enabled (by rescuers or executors), they claim the access rights.
     */
    bool public override inRescueMode;

    /// @dev Track proposed rescuer/executor
    address private _proposedNewRescuer;
    address private _proposedNewExecutor;

    constructor(address initialRescuer, address initialExecutor) {
        if (initialRescuer == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        if (initialExecutor == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        if (initialExecutor == initialRescuer) revert CommonEventsAndErrors.InvalidAddress();

        rescuer = initialRescuer;
        executor = initialExecutor;
    }

    /**
     * @notice Set the contract into or out of rescue mode.
     * Only the rescuers are allowed to set.
     */
    function setRescueMode(bool value) external override {
        if (msg.sender != rescuer) revert CommonEventsAndErrors.InvalidAccess();
        emit RescueModeSet(value);
        inRescueMode = value;
    }

    /**
     * @notice Proposes a new Rescuer.
     * Can only be called by the current rescuer.
     */
    function proposeNewRescuer(address account) external override {
        if (msg.sender != rescuer) revert CommonEventsAndErrors.InvalidAccess();
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        emit NewRescuerProposed(msg.sender, _proposedNewRescuer, account);
        _proposedNewRescuer = account;
    }

    /**
     * @notice Caller accepts the role as new Rescuer.
     * Can only be called by the proposed rescuer
     */
    function acceptRescuer() external override {
        if (msg.sender != _proposedNewRescuer) revert CommonEventsAndErrors.InvalidAccess();
        if (msg.sender == executor) revert CommonEventsAndErrors.InvalidAddress();

        emit NewRescuerAccepted(rescuer, msg.sender);
        rescuer = msg.sender;
        delete _proposedNewRescuer;
    }

    /**
     * @notice Proposes a new Executor.
     * Can only be called by the current executor or rescuer (if in resuce mode)
     */
    function proposeNewExecutor(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        emit NewExecutorProposed(executor, _proposedNewExecutor, account);
        _proposedNewExecutor = account;
    }

    /**
     * @notice Caller accepts the role as new Executor.
     * Can only be called by the proposed executor
     */
    function acceptExecutor() external override {
        if (msg.sender != _proposedNewExecutor) revert CommonEventsAndErrors.InvalidAccess();
        if (msg.sender == rescuer) revert CommonEventsAndErrors.InvalidAddress();

        emit NewExecutorAccepted(executor, msg.sender);
        executor = msg.sender;
        delete _proposedNewExecutor;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        uint256 _length = access.length;
        ExplicitAccess memory _access;
        for (uint256 i; i < _length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        if (inRescueMode) {
            // If we're in rescue mode, then only the rescuers can call
            return caller == rescuer;
        } else if (caller == executor || explicitFunctionAccess[caller][fnSelector]) {
            // If we're not in rescue mode, the executor can call all functions
            // or the caller has been given explicit access on this function
            return true;
        }
        return false;
    }

    /**
     * @notice Under normal operations, only the executors are allowed to call.
     * If 'rescue mode' has been enabled, then only the rescuers are allowed to call.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }

    /**
     * @notice Only the executors or rescuers can call.
     */
    modifier onlyInRescueMode() {
        if (!(inRescueMode && msg.sender == rescuer)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }

    modifier notInRescueMode() {
        if (inRescueMode) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}

File 46 of 46 : CompoundedInterest.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/interestRate/CompoundedInterest.sol)

import { ud } from "@prb/math/src/UD60x18.sol";

/**
 * @notice A maths library to calculate compounded interest
 */
library CompoundedInterest {
    uint256 public constant ONE_YEAR = 365 days;

    /// @notice FV = P*e^(r*t)
    /// @param principal Initial principal amount, 1e18 precision
    /// @param elapsed Number of seconds elapsed
    /// @param interestRate The interest rate per annum, 1e18 precision. eg 5% = 0.05e18
    function continuouslyCompounded(
        uint256 principal, 
        uint256 elapsed, 
        uint96 interestRate
    ) internal pure returns (uint256) {
        uint256 exponent = elapsed * interestRate / ONE_YEAR;
        return ud(principal).mul(
            ud(exponent).exp()
        ).unwrap();
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_initialRescuer","type":"address"},{"internalType":"address","name":"_initialExecutor","type":"address"},{"internalType":"address","name":"_circuitBreakerProxy","type":"address"},{"internalType":"address","name":"_templeToken","type":"address"},{"internalType":"address","name":"_daiToken","type":"address"},{"internalType":"uint256","name":"_maxLtvRatio","type":"uint256"},{"internalType":"address","name":"_interestRateModel","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"totalDebtAmount","type":"uint256"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"ExceededBorrowedAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"collateralValue","type":"uint256"},{"internalType":"uint256","name":"currentDaiDebt","type":"uint256"}],"name":"ExceededMaxLtv","type":"error"},{"inputs":[],"name":"ExpectedNonZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InvalidAccess","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PRBMath_MulDiv18_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Exp2_InputTooBig","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Exp_InputTooBig","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"BorrowPausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundedBy","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint128","name":"collateralAmount","type":"uint128"}],"name":"CollateralAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint128","name":"collateralAmount","type":"uint128"}],"name":"CollateralRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"ExplicitAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"interestRateModel","type":"address"}],"name":"InterestRateModelSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newInterestRate","type":"uint96"}],"name":"InterestRateUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint128","name":"collateralSeized","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"collateralValue","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"daiDebtWiped","type":"uint128"}],"name":"Liquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"LiquidationsPausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxLtvRatio","type":"uint256"}],"name":"MaxLtvRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"MinBorrowAmountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldExecutor","type":"address"},{"indexed":true,"internalType":"address","name":"newExecutor","type":"address"}],"name":"NewExecutorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldExecutor","type":"address"},{"indexed":true,"internalType":"address","name":"oldProposedExecutor","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedExecutor","type":"address"}],"name":"NewExecutorProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRescuer","type":"address"},{"indexed":true,"internalType":"address","name":"newRescuer","type":"address"}],"name":"NewRescuerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRescuer","type":"address"},{"indexed":true,"internalType":"address","name":"oldProposedRescuer","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedRescuer","type":"address"}],"name":"NewRescuerProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundedBy","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint128","name":"repayAmount","type":"uint128"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"RescueModeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"address","name":"treasuryReservesVault","type":"address"}],"name":"TlcStrategySet","type":"event"},{"inputs":[],"name":"acceptExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptRescuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountData","outputs":[{"components":[{"internalType":"uint128","name":"collateral","type":"uint128"},{"internalType":"uint128","name":"debtCheckpoint","type":"uint128"},{"internalType":"uint256","name":"interestAccumulator","type":"uint256"}],"internalType":"struct ITlcDataTypes.AccountData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountPosition","outputs":[{"components":[{"internalType":"uint128","name":"collateral","type":"uint128"},{"internalType":"uint128","name":"currentDebt","type":"uint128"},{"internalType":"uint128","name":"maxBorrow","type":"uint128"},{"internalType":"uint256","name":"healthFactor","type":"uint256"},{"internalType":"uint256","name":"loanToValueRatio","type":"uint256"}],"internalType":"struct ITlcDataTypes.AccountPosition","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"collateralAmount","type":"uint128"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"batchLiquidate","outputs":[{"internalType":"uint128","name":"totalCollateralClaimed","type":"uint128"},{"internalType":"uint128","name":"totalDebtWiped","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"recipient","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"circuitBreakerProxy","outputs":[{"internalType":"contract ITempleCircuitBreakerProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"computeLiquidity","outputs":[{"components":[{"internalType":"bool","name":"hasExceededMaxLtv","type":"bool"},{"internalType":"uint128","name":"collateral","type":"uint128"},{"internalType":"uint256","name":"collateralValue","type":"uint256"},{"internalType":"uint128","name":"currentDebt","type":"uint128"}],"internalType":"struct ITlcDataTypes.LiquidationStatus[]","name":"status","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"daiToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtTokenDetails","outputs":[{"components":[{"internalType":"uint96","name":"maxLtvRatio","type":"uint96"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"},{"internalType":"bool","name":"borrowsPaused","type":"bool"}],"internalType":"struct ITlcDataTypes.DebtTokenConfig","name":"","type":"tuple"},{"components":[{"internalType":"uint32","name":"interestAccumulatorUpdatedAt","type":"uint32"},{"internalType":"uint128","name":"totalDebt","type":"uint128"},{"internalType":"uint96","name":"interestRate","type":"uint96"},{"internalType":"uint256","name":"interestAccumulator","type":"uint256"}],"internalType":"struct ITlcDataTypes.DebtTokenData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"explicitFunctionAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDebtTokenCache","outputs":[{"components":[{"components":[{"internalType":"uint96","name":"maxLtvRatio","type":"uint96"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"},{"internalType":"bool","name":"borrowsPaused","type":"bool"}],"internalType":"struct ITlcDataTypes.DebtTokenConfig","name":"config","type":"tuple"},{"internalType":"uint128","name":"totalDebt","type":"uint128"},{"internalType":"uint96","name":"interestRate","type":"uint96"},{"internalType":"uint256","name":"interestAccumulator","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"trvDebtCeiling","type":"uint256"}],"internalType":"struct TempleLineOfCredit.DebtTokenCache","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inRescueMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBorrowAmount","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewRescuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refreshInterestRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"recipient","type":"address"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"repayAmount","type":"uint128"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"repayAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescuer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused","type":"bool"}],"name":"setBorrowPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"allowedCaller","type":"address"},{"components":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"internalType":"struct ITempleElevatedAccess.ExplicitAccess[]","name":"access","type":"tuple[]"}],"name":"setExplicitAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"interestRateModel","type":"address"}],"name":"setInterestRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused","type":"bool"}],"name":"setLiquidationsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxLtvRatio","type":"uint256"}],"name":"setMaxLtvRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"setMinBorrowAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setRescueMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTlcStrategy","type":"address"}],"name":"setTlcStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"templeToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tlcStrategy","outputs":[{"internalType":"contract ITlcStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebtPosition","outputs":[{"components":[{"internalType":"uint256","name":"utilizationRatio","type":"uint256"},{"internalType":"uint256","name":"borrowRate","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct ITlcDataTypes.TotalDebtPosition","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryReservesVault","outputs":[{"internalType":"contract ITreasuryReservesVault","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60e0604052600d8054610100600160881b031916693635c9adc5dea00000001790553480156200002e57600080fd5b50604051620054163803806200541683398101604081905262000051916200021a565b86866001600160a01b0382166200007b5760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038116620000a35760405163e6c4247b60e01b815260040160405180910390fd5b816001600160a01b0316816001600160a01b031603620000d65760405163e6c4247b60e01b815260040160405180910390fd5b600080546001600160a01b03199081166001600160a01b03948516179091556001805490911691831691909117905585811660c052848116608052831660a052670de0b6b3a76400008211156200014057604051633494a40d60e21b815260040160405180910390fd5b6001600160a01b0381166200016857604051631536c32360e21b815260040160405180910390fd5b60408051606080820183526001600160601b039094168082526001600160a01b03909316602080830182905260009284018390526c01000000000000000000000000909102909317600955600a805460ff19169055815160808101835263ffffffff4216808252938101829052918201526b033b2e3c9fd0803ce80000009201829052600b55600c5550620002a69350505050565b80516001600160a01b03811681146200021557600080fd5b919050565b600080600080600080600060e0888a0312156200023657600080fd5b6200024188620001fd565b96506200025160208901620001fd565b95506200026160408901620001fd565b94506200027160608901620001fd565b93506200028160808901620001fd565b925060a088015191506200029860c08901620001fd565b905092959891949750929550565b60805160a05160c0516150c96200034d60003960008181610540015281816116fc01526121b20152600081816107610152818161216c0152818161251e015281816126280152818161266a0152818161306f015281816130ec0152613a3201526000818161067a015281816113e60152818161146401528181611634015281816116b60152818161184401528181611c4201528181611cc40152611fe401526150c96000f3fe608060405234801561001057600080fd5b50600436106102925760003560e01c80638fb5053e11610160578063be415927116100d8578063d9a349c11161008c578063daeccc7911610071578063daeccc791461082f578063deb906e71461085d578063f46cf7f01461092c57600080fd5b8063d9a349c114610809578063d9a7ed5c1461081c57600080fd5b8063c34c08e5116100bd578063c34c08e5146107a9578063c3e7a9de146107c9578063d495f89f146107e957600080fd5b8063be41592714610783578063bfccf0ec1461079657600080fd5b8063a8cdda061161012f578063b3a3684711610114578063b3a368471461069c578063b74823bb146106af578063be22f5461461075c57600080fd5b8063a8cdda0614610662578063b1a9069c1461067557600080fd5b80638fb5053e146105ff578063939752bd14610612578063a356bad914610625578063a7229fd91461064f57600080fd5b806338a631831161020e5780638112c4af116101c2578063872ec463116101a7578063872ec463146105cc5780638bcd4016146105df5780638f6d0f11146105f257600080fd5b80638112c4af1461058857806381cf90711461059057600080fd5b80634b2adf17116101f35780634b2adf171461053b5780634ffd48f4146105625780637c27383b1461057557600080fd5b806338a63183146105045780634ac8eb5f1461052457600080fd5b80630a5db8a8116102655780631810fe7f1161024a5780631810fe7f146104cc5780631f211405146104e957806325e6b5e8146104f157600080fd5b80630a5db8a814610445578063101d16841461048757600080fd5b80630230192d14610297578063032ef9011461041557806307f184f11461042a578063089771b31461043d575b600080fd5b60408051606080820183526000808352602080840182905292840181905283516080808201865282825281850183905281860183905290830191909152835180830185526009546bffffffffffffffffffffffff80821683526c0100000000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff1682860152600a5460ff1615158287015285519283018652600b5463ffffffff8116845264010000000081046fffffffffffffffffffffffffffffffff16958401959095527401000000000000000000000000000000000000000090940490931693810193909352600c5490830152906040805183516bffffffffffffffffffffffff908116825260208086015173ffffffffffffffffffffffffffffffffffffffff168184015294830151151582840152835163ffffffff16606080840191909152948401516fffffffffffffffffffffffffffffffff1660808301529183015190911660a082015291015160c082015260e0015b60405180910390f35b610428610423366004614a2e565b610992565b005b610428610438366004614a6d565b610a41565b610428610b79565b600d546104669061010090046fffffffffffffffffffffffffffffffff1681565b6040516fffffffffffffffffffffffffffffffff909116815260200161040c565b6005546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161040c565b6003546104d99060ff1681565b604051901515815260200161040c565b610428610b8b565b6104286104ff366004614aaf565b610caa565b6000546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b61052d60075481565b60405190815260200161040c565b6104a77f000000000000000000000000000000000000000000000000000000000000000081565b610428610570366004614ae6565b610e6b565b610428610583366004614a6d565b610f4c565b610428611078565b6105a361059e366004614b01565b6111b8565b604080516fffffffffffffffffffffffffffffffff93841681529290911660208301520161040c565b6104286105da366004614aaf565b61153f565b6104286105ed366004614a6d565b61187d565b600d546104d99060ff1681565b61042861060d366004614b76565b611985565b610428610620366004614a2e565b611a9d565b61062d611b67565b604080518251815260208084015190820152918101519082015260600161040c565b61042861065d366004614b8f565b611bdc565b610428610670366004614aaf565b611e3b565b6104a77f000000000000000000000000000000000000000000000000000000000000000081565b6104286106aa366004614aaf565b61201e565b6106b76123fc565b60405161040c9190815180516bffffffffffffffffffffffff16825260208082015173ffffffffffffffffffffffffffffffffffffffff169083015260409081015115159082015261010081016fffffffffffffffffffffffffffffffff60208401511660608301526bffffffffffffffffffffffff6040840151166080830152606083015160a0830152608083015160c083015260a083015160e083015292915050565b6104a77f000000000000000000000000000000000000000000000000000000000000000081565b610428610791366004614a6d565b612451565b6104286107a4366004614bd0565b612716565b6001546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b6107dc6107d7366004614b01565b61291d565b60405161040c9190614c58565b6006546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b610428610817366004614a6d565b612a68565b61042861082a366004614a2e565b612bad565b6104d961083d366004614d03565b600260209081526000928352604080842090915290825290205460ff1681565b6108f361086b366004614a6d565b60408051606080820183526000808352602080840182905292840181905273ffffffffffffffffffffffffffffffffffffffff94909416845260088252928290208251938401835280546fffffffffffffffffffffffffffffffff80821686527001000000000000000000000000000000009091041691840191909152600101549082015290565b6040805182516fffffffffffffffffffffffffffffffff908116825260208085015190911690820152918101519082015260600161040c565b61093f61093a366004614a6d565b612c70565b6040805182516fffffffffffffffffffffffffffffffff9081168252602080850151821690830152838301511691810191909152606080830151908201526080918201519181019190915260a00161040c565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109e3576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051811515907f18f4a9a726c72020fd959c636d2fd464c6cefe90afeaea3c830b971614cf70b690600090a2600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a92576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116610adf576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035460405173ffffffffffffffffffffffffffffffffffffffff808416926101009004169033907f374d23b359cab0f8963c5c1715a6de7974f53af00aecc27de50d93906b70943e90600090a46003805473ffffffffffffffffffffffffffffffffffffffff909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b610b89610b84612d97565b612e4a565b565b60045473ffffffffffffffffffffffffffffffffffffffff163314610bdc576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff163303610c2d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154604051339173ffffffffffffffffffffffffffffffffffffffff16907fe963dc9c0d2165b080440a5d2665566142f2426b1ea15f3da8390b0fd336b06490600090a3600180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600480549091169055565b60035460ff1615610ce7576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff16600003610d33576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260086020526040812090610d61612d97565b90506000610d96828460000160109054906101000a90046fffffffffffffffffffffffffffffffff1685600101546001612f92565b9050806fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff161115610e17576040517f27c447850000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8083166004830152861660248201526044015b60405180910390fd5b82546fffffffffffffffffffffffffffffffff91869003828116700100000000000000000000000000000000029290911691909117835560608201516001840155610e6482863387612fe9565b5050505050565b610e99336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b610ecf576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff166101006fffffffffffffffffffffffffffffffff8416908102919091179091556040519081527fb6082fede9c667d0939952970ae580680601d1fc2ec7fe1d8ddf5c1ab4927d5b906020015b60405180910390a150565b60035460ff1615610f89576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610f93612d97565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260086020526040812080546001808301549495509193610ff892869270010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169190612f92565b9050806fffffffffffffffffffffffffffffffff16600003611046576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546fffffffffffffffffffffffffffffffff1682556060830151600183015561107283823387612fe9565b50505050565b600354610100900473ffffffffffffffffffffffffffffffffffffffff1633146110ce576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff16330361111f576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008054604051339273ffffffffffffffffffffffffffffffffffffffff909216917ff07131157ebea15896dc89264eb9a7572e67cc9fad6b855015730fcec10704e091a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055600380547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b600354600090819060ff16156111fa576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff1615611237576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182526000808252602082018190529181018290526060810182905290611263612d97565b9050600085815b818110156113af5788888281811061128457611284614d38565b90506020020160208101906112999190614a6d565b73ffffffffffffffffffffffffffffffffffffffff811660009081526008602052604090209093506112cb9085613207565b80519095501561139f576020808601516040808801516060808a015183516fffffffffffffffffffffffffffffffff95861681529586019290925292169083015273ffffffffffffffffffffffffffffffffffffffff8516917fe8310e1de285c70a341b4aa33ea9850fc9e6a2f13d1e4833a99edbbf007a654b910160405180910390a2602085015161135e9088614d96565b96508460600151866113709190614d96565b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604081208181556001015595505b6113a881614dc6565b905061126a565b506fffffffffffffffffffffffffffffffff861615611512576005546114219073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811691166fffffffffffffffffffffffffffffffff89166132e2565b6005546006546040517f5ceae9c400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301526fffffffffffffffffffffffffffffffff8a1660248301529182166044820152911690635ceae9c490606401600060405180830381600087803b1580156114d057600080fd5b505af11580156114e4573d6000803e3d6000fd5b50505050856fffffffffffffffffffffffffffffffff166007600082825461150c9190614dfe565b90915550505b6fffffffffffffffffffffffffffffffff851615611534576115348386613459565b505050509250929050565b60035460ff161561157c576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff166000036115c8576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040902080546fffffffffffffffffffffffffffffffff908116908416811015611679576040517fb2b3b53b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660048201526fffffffffffffffffffffffffffffffff85166024820152604401610e0e565b6040517f57f597b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301523360248301526fffffffffffffffffffffffffffffffff861660448301527f000000000000000000000000000000000000000000000000000000000000000016906357f597b790606401600060405180830381600087803b15801561174057600080fd5b505af1158015611754573d6000803e3d6000fd5b5050505083816117649190614e11565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff91821617835560078054918616916000906117b5908490614dfe565b90915550506040516fffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84169033907f5bb115afc193c15025432a5b5d0608d8b6191666f532aa1b34172bee74c0c7049060200160405180910390a361182a8261182561350f565b613564565b61107273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016846fffffffffffffffffffffffffffffffff87166135df565b6118ab336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6118e1576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8216907f7902cd1307c545e3f5782172612372bf997a93698917ced12b2f83d86e347d0c90600090a2600061192c612d97565b805173ffffffffffffffffffffffffffffffffffffffff84166020909101819052600980546bffffffffffffffffffffffff166c01000000000000000000000000909202919091179055905061198181612e4a565b5050565b6119b3336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6119e9576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b670de0b6b3a7640000811115611a2b576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527f97a2b3b447f10c044556f6bee068ba4907d386ce7a2f5ccf193b9e9beabba0c89060200160405180910390a1600980547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff92909216919091179055565b611acb336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b611b01576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405181151581527fa1f4a6e2dfc84e852c5dd5b715b3ed1834e8b6017e6bfb3a59d61dbce99574d29060200160405180910390a1600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b611b8b60405180606001604052806000815260200160008152602001600081525090565b6000611b9561350f565b9050611ba081613635565b82526040808201516bffffffffffffffffffffffff16602080850191909152909101516fffffffffffffffffffffffffffffffff169082015290565b611c0a336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b611c40576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611dae576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d449190614e3a565b905060075481611d549190614dfe565b821115611dac576040517fb2b3b53b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260248101839052604401610e0e565b505b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f909683604051611e0d91815260200190565b60405180910390a3611e3673ffffffffffffffffffffffffffffffffffffffff841683836135df565b505050565b60035460ff1615611e78576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff16600003611ec4576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516fffffffffffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff82169033907fa294b8c659c4fead0fea8156278762692bd09dced6313207eba83a4404d1365e9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff811660009081526008602052604081208054849290611f6a9084906fffffffffffffffffffffffffffffffff16614d96565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550816fffffffffffffffffffffffffffffffff1660076000828254611fc49190614e53565b90915550611981905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633306fffffffffffffffffffffffffffffffff861661367d565b60035460ff161561205b576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d546fffffffffffffffffffffffffffffffff610100909104811690831610156120d757600d546040517f6d400e380000000000000000000000000000000000000000000000000000000081526101009091046fffffffffffffffffffffffffffffffff908116600483015283166024820152604401610e0e565b336000908152600860205260408120906120ef612d97565b8051604001519091501561212f576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f57f597b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301523360248301526fffffffffffffffffffffffffffffffff861660448301527f000000000000000000000000000000000000000000000000000000000000000016906357f597b790606401600060405180830381600087803b1580156121f657600080fd5b505af115801561220a573d6000803e3d6000fd5b50505050600084612242838560000160109054906101000a90046fffffffffffffffffffffffffffffffff1686600101546000612f92565b61224c9190614d96565b83546fffffffffffffffffffffffffffffffff808316700100000000000000000000000000000000029116178455606083015160018501556020830151909150612297908690614d96565b6fffffffffffffffffffffffffffffffff1660208301819052600b80547fffffffffffffffffffffffff00000000000000000000000000000000ffffffff166401000000009092029190911790556122ee82612e4a565b506040516fffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84169033907f5fee80056e42d3f27fe8a849b24eac30fcc58726838c192dbda4bc4dc46a5d3e9060200160405180910390a36123588282613564565b6006546040517f2642e7ed0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8616600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015290911690632642e7ed906044015b600060405180830381600087803b1580156123de57600080fd5b505af11580156123f2573d6000803e3d6000fd5b5050505050505050565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a081019190915261244c61350f565b905090565b61247f336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6124b5576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116919091179091556005541680156125465761254673ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168260006136db565b600654604080517f101d1684000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163101d16849160048083019260209291908290030181865afa1580156125b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125da9190614e66565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691909117909155909150612650907f0000000000000000000000000000000000000000000000000000000000000000168260006136db565b6126b173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6132e2565b8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fa93dadf034ad1899d178bd48d8d0af412f02457dc5e74580e7217fcf1456cea160405160405180910390a3611e36610b84612d97565b612744336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b61277a576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166127c7576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260008082526020820152819060005b82811015612915578484828181106127fa576127fa614d38565b9050604002018036038101906128109190614eb2565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a460208281015173ffffffffffffffffffffffffffffffffffffffff8816600090815260028352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561290e81614dc6565b90506127e0565b505050505050565b6060818067ffffffffffffffff81111561293957612939614e83565b6040519080825280602002602001820160405280156129a957816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816129575790505b50915060006129b661350f565b905060005b82811015612a5f57612a31600860008888858181106129dc576129dc614d38565b90506020020160208101906129f19190614a6d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002083613207565b848281518110612a4357612a43614d38565b602002602001018190525080612a5890614dc6565b90506129bb565b50505092915050565b612a96336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b612acc576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116612b19576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045460015460405173ffffffffffffffffffffffffffffffffffffffff8085169381169216907f4857570a90fe0a0fc580e89a287e77576141ac8e2e8b3710cd26db44f44156c190600090a4600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612bdb336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b612c11576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527f9620f507aec770d0d1dfc5054ec14baa7cc0fa4d49a042a5feb1e11aa1ccb16790602001610f41565b6040805160a08101825260008082526020808301829052828401829052606083018290526080830182905273ffffffffffffffffffffffffffffffffffffffff85168252600890529182209091612cc561350f565b82546fffffffffffffffffffffffffffffffff8082168652600180860154939450612d0a93859370010000000000000000000000000000000090049092169190612f92565b6fffffffffffffffffffffffffffffffff1660208401528251612d2e90829061385d565b6fffffffffffffffffffffffffffffffff908116604085015283516020850151612d5d928492811691166138b4565b606084015282516020840151612d8a9183916fffffffffffffffffffffffffffffffff9182169116613925565b6080840152509092915050565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a0810191909152612de881613951565b15612e4757600b805460208301516fffffffffffffffffffffffffffffffff16640100000000027fffffffffffffffffffffffff000000000000000000000000000000000000000090911663ffffffff4216171790556060810151600c555b90565b80516020015160009073ffffffffffffffffffffffffffffffffffffffff166390d3b379612e7784613635565b6040518263ffffffff1660e01b8152600401612e9591815260200190565b602060405180830381865afa158015612eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ed69190614f36565b9050806bffffffffffffffffffffffff1682604001516bffffffffffffffffffffffff1614611981576040516bffffffffffffffffffffffff821681527f5bac5d73010c05db73a6f017d97786be2d011fdeeda45a3ad49ddfd4c4d836ee9060200160405180910390a16bffffffffffffffffffffffff16604091909101819052600b805473ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000909202919091179055565b60006fffffffffffffffffffffffffffffffff841615612fdb57612fd6612fd1856fffffffffffffffffffffffffffffffff1687606001518686613bc1565b613bfd565b612fde565b60005b90505b949350505050565b612ff38484613459565b6040516fffffffffffffffffffffffffffffffff8416815273ffffffffffffffffffffffffffffffffffffffff80831691908416907fb81daa514d39133aa6960d430d2f15f0c150cb0e4d51c8ff506080c34985f6009060200160405180910390a36130a973ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683306fffffffffffffffffffffffffffffffff871661367d565b6005546006546040517f5ceae9c400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301526fffffffffffffffffffffffffffffffff871660248301529182166044820152911690635ceae9c4906064016123c4565b60035460009060ff1615613175575060005473ffffffffffffffffffffffffffffffffffffffff838116911614613201565b60015473ffffffffffffffffffffffffffffffffffffffff848116911614806131f0575073ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff165b156131fd57506001613201565b5060005b92915050565b604080516080810182526000808252918101829052606081019190915282546fffffffffffffffffffffffffffffffff80821660208401526001808601546132679386937001000000000000000000000000000000009091041691612f92565b6fffffffffffffffffffffffffffffffff908116606083015260808301516020830151670de0b6b3a76400009261329f929116614f64565b6132a99190614faa565b604082015260208101516132be90839061385d565b60608201516fffffffffffffffffffffffffffffffff918216911611815292915050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015613358573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061337c9190614e3a565b9050611072847f095ea7b300000000000000000000000000000000000000000000000000000000856133ae8686614e53565b60405173ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613c4c565b806fffffffffffffffffffffffffffffffff16600003613477575050565b600082602001516fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff16116134b457818360200151036134b7565b60005b6fffffffffffffffffffffffffffffffff1660208401819052600b80547fffffffffffffffffffffffff00000000000000000000000000000000ffffffff166401000000009092029190911790555061198182612e4a565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a081019190915261356081613951565b5090565b60006135708383613207565b805190915015611e36576020810151604080830151606084015191517f049f16120000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff9384166004820152602481019190915291166044820152606401610e0e565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611e369084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016133d7565b60008160a001516000146136755761367082602001516fffffffffffffffffffffffffffffffff16670de0b6b3a76400008460a00151613d5b565b613201565b600092915050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526110729085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016133d7565b80158061377b57506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015613755573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137799190614e3a565b155b613807576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610e0e565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611e369084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016133d7565b60006138ad612fd18460800151846fffffffffffffffffffffffffffffffff166138879190614f64565b8551516bffffffffffffffffffffffff166ec097ce7bc90715b34b9f1000000000613d5b565b9392505050565b600081156138fc576138f78460800151846138cf9190614f64565b8551516bffffffffffffffffffffffff166138f2670de0b6b3a764000086614f64565b613d5b565b612fe1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff949350505050565b600082156138fc576138f7826ec097ce7bc90715b34b9f10000000008660800151866138f29190614f64565b60408051606080820183526009546bffffffffffffffffffffffff808216845273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009092048216602080860191909152600a5460ff16151585870152938652600c5492860192909252600b546fffffffffffffffffffffffffffffffff6401000000008204169386019390935274010000000000000000000000000000000000000000909204168383015260055460065492517fdd07270400000000000000000000000000000000000000000000000000000000815292821660048401527f000000000000000000000000000000000000000000000000000000000000000082166024840152600092911690819063dd07270490604401602060405180830381865afa158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614e3a565b8360a00181815250508073ffffffffffffffffffffffffffffffffffffffff16639ed384f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015613afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b229190614f36565b6bffffffffffffffffffffffff16608084015250600b5463ffffffff908116420390811615613bbb57600191506000613b748263ffffffff1685604001518660600151613e669092919063ffffffff16565b9050613b9e612fd18286602001516fffffffffffffffffffffffffffffffff168760600151613d5b565b6fffffffffffffffffffffffffffffffff16602085015260608401525b50919050565b6000613bce858585613d5b565b9050818015613bed575060008380613be857613be8614f7b565b858709115b15612fe157612fde600182614e53565b60006fffffffffffffffffffffffffffffffff821115613560576040517fe0fb6a7c00000000000000000000000000000000000000000000000000000000815260048101839052602401610e0e565b6000613cae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613eb19092919063ffffffff16565b9050805160001480613ccf575080806020019051810190613ccf9190614fe5565b611e36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610e0e565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003613db357838281613da957613da9614f7b565b04925050506138ad565b838110613dfd576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610e0e565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000806301e13380613e866bffffffffffffffffffffffff851686614f64565b613e909190614faa565b9050613ea8612e47613ea183613ec0565b8790613f27565b95945050505050565b6060612fe18484600085613f36565b600081680736ea4425c11ac630811115613f09576040517f1af63aca00000000000000000000000000000000000000000000000000000000815260048101849052602401610e0e565b6714057b7ef767814f8102612fe1670de0b6b3a7640000820461404f565b60006138ad612e4784846140be565b606082471015613fc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610e0e565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613ff19190615026565b60006040518083038185875af1925050503d806000811461402e576040519150601f19603f3d011682016040523d82523d6000602084013e614033565b606091505b5091509150614044878383876141c5565b979650505050505050565b600081680a688906bd8affffff811115614098576040517fb3b6ba1f00000000000000000000000000000000000000000000000000000000815260048101849052602401610e0e565b60006140b0670de0b6b3a7640000604084901b614faa565b9050612fe1612e4782614265565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848609848602925082811083820303915050806000036141105750670de0b6b3a764000090049050613201565b670de0b6b3a7640000811061415b576040517f5173648d0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610e0e565b6000670de0b6b3a7640000858709620400008185030493109091037d40000000000000000000000000000000000000000000000000000000000002919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b6060831561425b5782516000036142545773ffffffffffffffffffffffffffffffffffffffff85163b614254576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e0e565b5081612fe1565b612fe183836149d9565b7780000000000000000000000000000000000000000000000067ff00000000000000821615614386576780000000000000008216156142ad5768016a09e667f3bcc9090260401c5b6740000000000000008216156142cc576801306fe0a31b7152df0260401c5b6720000000000000008216156142eb576801172b83c7d517adce0260401c5b67100000000000000082161561430a5768010b5586cf9890f62a0260401c5b670800000000000000821615614329576801059b0d31585743ae0260401c5b67040000000000000082161561434857680102c9a3e778060ee70260401c5b6702000000000000008216156143675768010163da9fb33356d80260401c5b67010000000000000082161561438657680100b1afa5abcbed610260401c5b66ff0000000000008216156144855766800000000000008216156143b35768010058c86da1c09ea20260401c5b66400000000000008216156143d1576801002c605e2e8cec500260401c5b66200000000000008216156143ef57680100162f3904051fa10260401c5b661000000000000082161561440d576801000b175effdc76ba0260401c5b660800000000000082161561442b57680100058ba01fb9f96d0260401c5b66040000000000008216156144495768010002c5cc37da94920260401c5b6602000000000000821615614467576801000162e525ee05470260401c5b66010000000000008216156144855768010000b17255775c040260401c5b65ff000000000082161561457b57658000000000008216156144b0576801000058b91b5bc9ae0260401c5b654000000000008216156144cd57680100002c5c89d5ec6d0260401c5b652000000000008216156144ea5768010000162e43f4f8310260401c5b6510000000000082161561450757680100000b1721bcfc9a0260401c5b650800000000008216156145245768010000058b90cf1e6e0260401c5b65040000000000821615614541576801000002c5c863b73f0260401c5b6502000000000082161561455e57680100000162e430e5a20260401c5b6501000000000082161561457b576801000000b1721835510260401c5b64ff00000000821615614668576480000000008216156145a457680100000058b90c0b490260401c5b6440000000008216156145c05768010000002c5c8601cc0260401c5b6420000000008216156145dc576801000000162e42fff00260401c5b6410000000008216156145f85768010000000b17217fbb0260401c5b640800000000821615614614576801000000058b90bfce0260401c5b64040000000082161561463057680100000002c5c85fe30260401c5b64020000000082161561464c5768010000000162e42ff10260401c5b64010000000082161561466857680100000000b17217f80260401c5b63ff00000082161561474c57638000000082161561468f5768010000000058b90bfc0260401c5b63400000008216156146aa576801000000002c5c85fe0260401c5b63200000008216156146c557680100000000162e42ff0260401c5b63100000008216156146e0576801000000000b17217f0260401c5b63080000008216156146fb57680100000000058b90c00260401c5b63040000008216156147165768010000000002c5c8600260401c5b6302000000821615614731576801000000000162e4300260401c5b630100000082161561474c5768010000000000b172180260401c5b62ff00008216156148275762800000821615614771576801000000000058b90c0260401c5b6240000082161561478b57680100000000002c5c860260401c5b622000008216156147a55768010000000000162e430260401c5b621000008216156147bf57680100000000000b17210260401c5b620800008216156147d95768010000000000058b910260401c5b620400008216156147f3576801000000000002c5c80260401c5b6202000082161561480d57680100000000000162e40260401c5b62010000821615614827576801000000000000b1720260401c5b61ff008216156148f95761800082161561484a57680100000000000058b90260401c5b6140008216156148635768010000000000002c5d0260401c5b61200082161561487c576801000000000000162e0260401c5b6110008216156148955768010000000000000b170260401c5b6108008216156148ae576801000000000000058c0260401c5b6104008216156148c757680100000000000002c60260401c5b6102008216156148e057680100000000000001630260401c5b6101008216156148f957680100000000000000b10260401c5b60ff8216156149c257608082161561491a57680100000000000000590260401c5b6040821615614932576801000000000000002c0260401c5b602082161561494a57680100000000000000160260401c5b6010821615614962576801000000000000000b0260401c5b600882161561497a57680100000000000000060260401c5b600482161561499257680100000000000000030260401c5b60028216156149aa57680100000000000000010260401c5b60018216156149c257680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b8151156149e95781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0e9190615042565b8015158114614a2b57600080fd5b50565b600060208284031215614a4057600080fd5b81356138ad81614a1d565b73ffffffffffffffffffffffffffffffffffffffff81168114614a2b57600080fd5b600060208284031215614a7f57600080fd5b81356138ad81614a4b565b80356fffffffffffffffffffffffffffffffff81168114614aaa57600080fd5b919050565b60008060408385031215614ac257600080fd5b614acb83614a8a565b91506020830135614adb81614a4b565b809150509250929050565b600060208284031215614af857600080fd5b6138ad82614a8a565b60008060208385031215614b1457600080fd5b823567ffffffffffffffff80821115614b2c57600080fd5b818501915085601f830112614b4057600080fd5b813581811115614b4f57600080fd5b8660208260051b8501011115614b6457600080fd5b60209290920196919550909350505050565b600060208284031215614b8857600080fd5b5035919050565b600080600060608486031215614ba457600080fd5b8335614baf81614a4b565b92506020840135614bbf81614a4b565b929592945050506040919091013590565b600080600060408486031215614be557600080fd5b8335614bf081614a4b565b9250602084013567ffffffffffffffff80821115614c0d57600080fd5b818601915086601f830112614c2157600080fd5b813581811115614c3057600080fd5b8760208260061b8501011115614c4557600080fd5b6020830194508093505050509250925092565b602080825282518282018190526000919060409081850190868401855b82811015614cc6578151805115158552868101516fffffffffffffffffffffffffffffffff908116888701528682015187870152606091820151169085015260809093019290850190600101614c75565b5091979650505050505050565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614aaa57600080fd5b60008060408385031215614d1657600080fd5b8235614d2181614a4b565b9150614d2f60208401614cd3565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff818116838216019080821115614dbf57614dbf614d67565b5092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614df757614df7614d67565b5060010190565b8181038181111561320157613201614d67565b6fffffffffffffffffffffffffffffffff828116828216039080821115614dbf57614dbf614d67565b600060208284031215614e4c57600080fd5b5051919050565b8082018082111561320157613201614d67565b600060208284031215614e7857600080fd5b81516138ad81614a4b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060408284031215614ec457600080fd5b6040516040810181811067ffffffffffffffff82111715614f0e577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614f1a83614cd3565b81526020830135614f2a81614a1d565b60208201529392505050565b600060208284031215614f4857600080fd5b81516bffffffffffffffffffffffff811681146138ad57600080fd5b808202811582820484141761320157613201614d67565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614fe0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208284031215614ff757600080fd5b81516138ad81614a1d565b60005b8381101561501d578181015183820152602001615005565b50506000910152565b60008251615038818460208701615002565b9190910192915050565b6020815260008251806020840152615061816040850160208701615002565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea26469706673582212208839514266137ae35124e7fd5a779701e64e0b129ac9b43120bc98a16e91694d64736f6c634300081300330000000000000000000000009f90430179d9b67341bfa50559bc7b8e35629f1b000000000000000000000000b1e8ab0a81aadf632a653e46caeda1593d71d14300000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a480000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b70000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000009498ab765beed8292938937079ac56080b8a179d

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102925760003560e01c80638fb5053e11610160578063be415927116100d8578063d9a349c11161008c578063daeccc7911610071578063daeccc791461082f578063deb906e71461085d578063f46cf7f01461092c57600080fd5b8063d9a349c114610809578063d9a7ed5c1461081c57600080fd5b8063c34c08e5116100bd578063c34c08e5146107a9578063c3e7a9de146107c9578063d495f89f146107e957600080fd5b8063be41592714610783578063bfccf0ec1461079657600080fd5b8063a8cdda061161012f578063b3a3684711610114578063b3a368471461069c578063b74823bb146106af578063be22f5461461075c57600080fd5b8063a8cdda0614610662578063b1a9069c1461067557600080fd5b80638fb5053e146105ff578063939752bd14610612578063a356bad914610625578063a7229fd91461064f57600080fd5b806338a631831161020e5780638112c4af116101c2578063872ec463116101a7578063872ec463146105cc5780638bcd4016146105df5780638f6d0f11146105f257600080fd5b80638112c4af1461058857806381cf90711461059057600080fd5b80634b2adf17116101f35780634b2adf171461053b5780634ffd48f4146105625780637c27383b1461057557600080fd5b806338a63183146105045780634ac8eb5f1461052457600080fd5b80630a5db8a8116102655780631810fe7f1161024a5780631810fe7f146104cc5780631f211405146104e957806325e6b5e8146104f157600080fd5b80630a5db8a814610445578063101d16841461048757600080fd5b80630230192d14610297578063032ef9011461041557806307f184f11461042a578063089771b31461043d575b600080fd5b60408051606080820183526000808352602080840182905292840181905283516080808201865282825281850183905281860183905290830191909152835180830185526009546bffffffffffffffffffffffff80821683526c0100000000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff1682860152600a5460ff1615158287015285519283018652600b5463ffffffff8116845264010000000081046fffffffffffffffffffffffffffffffff16958401959095527401000000000000000000000000000000000000000090940490931693810193909352600c5490830152906040805183516bffffffffffffffffffffffff908116825260208086015173ffffffffffffffffffffffffffffffffffffffff168184015294830151151582840152835163ffffffff16606080840191909152948401516fffffffffffffffffffffffffffffffff1660808301529183015190911660a082015291015160c082015260e0015b60405180910390f35b610428610423366004614a2e565b610992565b005b610428610438366004614a6d565b610a41565b610428610b79565b600d546104669061010090046fffffffffffffffffffffffffffffffff1681565b6040516fffffffffffffffffffffffffffffffff909116815260200161040c565b6005546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161040c565b6003546104d99060ff1681565b604051901515815260200161040c565b610428610b8b565b6104286104ff366004614aaf565b610caa565b6000546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b61052d60075481565b60405190815260200161040c565b6104a77f00000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a48081565b610428610570366004614ae6565b610e6b565b610428610583366004614a6d565b610f4c565b610428611078565b6105a361059e366004614b01565b6111b8565b604080516fffffffffffffffffffffffffffffffff93841681529290911660208301520161040c565b6104286105da366004614aaf565b61153f565b6104286105ed366004614a6d565b61187d565b600d546104d99060ff1681565b61042861060d366004614b76565b611985565b610428610620366004614a2e565b611a9d565b61062d611b67565b604080518251815260208084015190820152918101519082015260600161040c565b61042861065d366004614b8f565b611bdc565b610428610670366004614aaf565b611e3b565b6104a77f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b781565b6104286106aa366004614aaf565b61201e565b6106b76123fc565b60405161040c9190815180516bffffffffffffffffffffffff16825260208082015173ffffffffffffffffffffffffffffffffffffffff169083015260409081015115159082015261010081016fffffffffffffffffffffffffffffffff60208401511660608301526bffffffffffffffffffffffff6040840151166080830152606083015160a0830152608083015160c083015260a083015160e083015292915050565b6104a77f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f81565b610428610791366004614a6d565b612451565b6104286107a4366004614bd0565b612716565b6001546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b6107dc6107d7366004614b01565b61291d565b60405161040c9190614c58565b6006546104a79073ffffffffffffffffffffffffffffffffffffffff1681565b610428610817366004614a6d565b612a68565b61042861082a366004614a2e565b612bad565b6104d961083d366004614d03565b600260209081526000928352604080842090915290825290205460ff1681565b6108f361086b366004614a6d565b60408051606080820183526000808352602080840182905292840181905273ffffffffffffffffffffffffffffffffffffffff94909416845260088252928290208251938401835280546fffffffffffffffffffffffffffffffff80821686527001000000000000000000000000000000009091041691840191909152600101549082015290565b6040805182516fffffffffffffffffffffffffffffffff908116825260208085015190911690820152918101519082015260600161040c565b61093f61093a366004614a6d565b612c70565b6040805182516fffffffffffffffffffffffffffffffff9081168252602080850151821690830152838301511691810191909152606080830151908201526080918201519181019190915260a00161040c565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109e3576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051811515907f18f4a9a726c72020fd959c636d2fd464c6cefe90afeaea3c830b971614cf70b690600090a2600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a92576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116610adf576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035460405173ffffffffffffffffffffffffffffffffffffffff808416926101009004169033907f374d23b359cab0f8963c5c1715a6de7974f53af00aecc27de50d93906b70943e90600090a46003805473ffffffffffffffffffffffffffffffffffffffff909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b610b89610b84612d97565b612e4a565b565b60045473ffffffffffffffffffffffffffffffffffffffff163314610bdc576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff163303610c2d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154604051339173ffffffffffffffffffffffffffffffffffffffff16907fe963dc9c0d2165b080440a5d2665566142f2426b1ea15f3da8390b0fd336b06490600090a3600180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600480549091169055565b60035460ff1615610ce7576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff16600003610d33576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600090815260086020526040812090610d61612d97565b90506000610d96828460000160109054906101000a90046fffffffffffffffffffffffffffffffff1685600101546001612f92565b9050806fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff161115610e17576040517f27c447850000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8083166004830152861660248201526044015b60405180910390fd5b82546fffffffffffffffffffffffffffffffff91869003828116700100000000000000000000000000000000029290911691909117835560608201516001840155610e6482863387612fe9565b5050505050565b610e99336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b610ecf576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff166101006fffffffffffffffffffffffffffffffff8416908102919091179091556040519081527fb6082fede9c667d0939952970ae580680601d1fc2ec7fe1d8ddf5c1ab4927d5b906020015b60405180910390a150565b60035460ff1615610f89576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610f93612d97565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260086020526040812080546001808301549495509193610ff892869270010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169190612f92565b9050806fffffffffffffffffffffffffffffffff16600003611046576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546fffffffffffffffffffffffffffffffff1682556060830151600183015561107283823387612fe9565b50505050565b600354610100900473ffffffffffffffffffffffffffffffffffffffff1633146110ce576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff16330361111f576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008054604051339273ffffffffffffffffffffffffffffffffffffffff909216917ff07131157ebea15896dc89264eb9a7572e67cc9fad6b855015730fcec10704e091a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055600380547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b600354600090819060ff16156111fa576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff1615611237576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182526000808252602082018190529181018290526060810182905290611263612d97565b9050600085815b818110156113af5788888281811061128457611284614d38565b90506020020160208101906112999190614a6d565b73ffffffffffffffffffffffffffffffffffffffff811660009081526008602052604090209093506112cb9085613207565b80519095501561139f576020808601516040808801516060808a015183516fffffffffffffffffffffffffffffffff95861681529586019290925292169083015273ffffffffffffffffffffffffffffffffffffffff8516917fe8310e1de285c70a341b4aa33ea9850fc9e6a2f13d1e4833a99edbbf007a654b910160405180910390a2602085015161135e9088614d96565b96508460600151866113709190614d96565b73ffffffffffffffffffffffffffffffffffffffff841660009081526008602052604081208181556001015595505b6113a881614dc6565b905061126a565b506fffffffffffffffffffffffffffffffff861615611512576005546114219073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b7811691166fffffffffffffffffffffffffffffffff89166132e2565b6005546006546040517f5ceae9c400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b7811660048301526fffffffffffffffffffffffffffffffff8a1660248301529182166044820152911690635ceae9c490606401600060405180830381600087803b1580156114d057600080fd5b505af11580156114e4573d6000803e3d6000fd5b50505050856fffffffffffffffffffffffffffffffff166007600082825461150c9190614dfe565b90915550505b6fffffffffffffffffffffffffffffffff851615611534576115348386613459565b505050509250929050565b60035460ff161561157c576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff166000036115c8576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040902080546fffffffffffffffffffffffffffffffff908116908416811015611679576040517fb2b3b53b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b71660048201526fffffffffffffffffffffffffffffffff85166024820152604401610e0e565b6040517f57f597b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b7811660048301523360248301526fffffffffffffffffffffffffffffffff861660448301527f00000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a48016906357f597b790606401600060405180830381600087803b15801561174057600080fd5b505af1158015611754573d6000803e3d6000fd5b5050505083816117649190614e11565b82547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff91821617835560078054918616916000906117b5908490614dfe565b90915550506040516fffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84169033907f5bb115afc193c15025432a5b5d0608d8b6191666f532aa1b34172bee74c0c7049060200160405180910390a361182a8261182561350f565b613564565b61107273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b716846fffffffffffffffffffffffffffffffff87166135df565b6118ab336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6118e1576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8216907f7902cd1307c545e3f5782172612372bf997a93698917ced12b2f83d86e347d0c90600090a2600061192c612d97565b805173ffffffffffffffffffffffffffffffffffffffff84166020909101819052600980546bffffffffffffffffffffffff166c01000000000000000000000000909202919091179055905061198181612e4a565b5050565b6119b3336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6119e9576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b670de0b6b3a7640000811115611a2b576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527f97a2b3b447f10c044556f6bee068ba4907d386ce7a2f5ccf193b9e9beabba0c89060200160405180910390a1600980547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff92909216919091179055565b611acb336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b611b01576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405181151581527fa1f4a6e2dfc84e852c5dd5b715b3ed1834e8b6017e6bfb3a59d61dbce99574d29060200160405180910390a1600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b611b8b60405180606001604052806000815260200160008152602001600081525090565b6000611b9561350f565b9050611ba081613635565b82526040808201516bffffffffffffffffffffffff16602080850191909152909101516fffffffffffffffffffffffffffffffff169082015290565b611c0a336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b611c40576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b773ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611dae576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b773ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d449190614e3a565b905060075481611d549190614dfe565b821115611dac576040517fb2b3b53b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015260248101839052604401610e0e565b505b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f909683604051611e0d91815260200190565b60405180910390a3611e3673ffffffffffffffffffffffffffffffffffffffff841683836135df565b505050565b60035460ff1615611e78576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff16600003611ec4576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516fffffffffffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff82169033907fa294b8c659c4fead0fea8156278762692bd09dced6313207eba83a4404d1365e9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff811660009081526008602052604081208054849290611f6a9084906fffffffffffffffffffffffffffffffff16614d96565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550816fffffffffffffffffffffffffffffffff1660076000828254611fc49190614e53565b90915550611981905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b71633306fffffffffffffffffffffffffffffffff861661367d565b60035460ff161561205b576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d546fffffffffffffffffffffffffffffffff610100909104811690831610156120d757600d546040517f6d400e380000000000000000000000000000000000000000000000000000000081526101009091046fffffffffffffffffffffffffffffffff908116600483015283166024820152604401610e0e565b336000908152600860205260408120906120ef612d97565b8051604001519091501561212f576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f57f597b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f811660048301523360248301526fffffffffffffffffffffffffffffffff861660448301527f00000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a48016906357f597b790606401600060405180830381600087803b1580156121f657600080fd5b505af115801561220a573d6000803e3d6000fd5b50505050600084612242838560000160109054906101000a90046fffffffffffffffffffffffffffffffff1686600101546000612f92565b61224c9190614d96565b83546fffffffffffffffffffffffffffffffff808316700100000000000000000000000000000000029116178455606083015160018501556020830151909150612297908690614d96565b6fffffffffffffffffffffffffffffffff1660208301819052600b80547fffffffffffffffffffffffff00000000000000000000000000000000ffffffff166401000000009092029190911790556122ee82612e4a565b506040516fffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84169033907f5fee80056e42d3f27fe8a849b24eac30fcc58726838c192dbda4bc4dc46a5d3e9060200160405180910390a36123588282613564565b6006546040517f2642e7ed0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8616600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015290911690632642e7ed906044015b600060405180830381600087803b1580156123de57600080fd5b505af11580156123f2573d6000803e3d6000fd5b5050505050505050565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a081019190915261244c61350f565b905090565b61247f336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b6124b5576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116919091179091556005541680156125465761254673ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f168260006136db565b600654604080517f101d1684000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163101d16849160048083019260209291908290030181865afa1580156125b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125da9190614e66565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691909117909155909150612650907f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f168260006136db565b6126b173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f16827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6132e2565b8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fa93dadf034ad1899d178bd48d8d0af412f02457dc5e74580e7217fcf1456cea160405160405180910390a3611e36610b84612d97565b612744336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b61277a576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166127c7576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805180820190915260008082526020820152819060005b82811015612915578484828181106127fa576127fa614d38565b9050604002018036038101906128109190614eb2565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a460208281015173ffffffffffffffffffffffffffffffffffffffff8816600090815260028352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561290e81614dc6565b90506127e0565b505050505050565b6060818067ffffffffffffffff81111561293957612939614e83565b6040519080825280602002602001820160405280156129a957816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816129575790505b50915060006129b661350f565b905060005b82811015612a5f57612a31600860008888858181106129dc576129dc614d38565b90506020020160208101906129f19190614a6d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002083613207565b848281518110612a4357612a43614d38565b602002602001018190525080612a5890614dc6565b90506129bb565b50505092915050565b612a96336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b612acc576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116612b19576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60045460015460405173ffffffffffffffffffffffffffffffffffffffff8085169381169216907f4857570a90fe0a0fc580e89a287e77576141ac8e2e8b3710cd26db44f44156c190600090a4600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b612bdb336000357fffffffff0000000000000000000000000000000000000000000000000000000016613143565b612c11576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527f9620f507aec770d0d1dfc5054ec14baa7cc0fa4d49a042a5feb1e11aa1ccb16790602001610f41565b6040805160a08101825260008082526020808301829052828401829052606083018290526080830182905273ffffffffffffffffffffffffffffffffffffffff85168252600890529182209091612cc561350f565b82546fffffffffffffffffffffffffffffffff8082168652600180860154939450612d0a93859370010000000000000000000000000000000090049092169190612f92565b6fffffffffffffffffffffffffffffffff1660208401528251612d2e90829061385d565b6fffffffffffffffffffffffffffffffff908116604085015283516020850151612d5d928492811691166138b4565b606084015282516020840151612d8a9183916fffffffffffffffffffffffffffffffff9182169116613925565b6080840152509092915050565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a0810191909152612de881613951565b15612e4757600b805460208301516fffffffffffffffffffffffffffffffff16640100000000027fffffffffffffffffffffffff000000000000000000000000000000000000000090911663ffffffff4216171790556060810151600c555b90565b80516020015160009073ffffffffffffffffffffffffffffffffffffffff166390d3b379612e7784613635565b6040518263ffffffff1660e01b8152600401612e9591815260200190565b602060405180830381865afa158015612eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ed69190614f36565b9050806bffffffffffffffffffffffff1682604001516bffffffffffffffffffffffff1614611981576040516bffffffffffffffffffffffff821681527f5bac5d73010c05db73a6f017d97786be2d011fdeeda45a3ad49ddfd4c4d836ee9060200160405180910390a16bffffffffffffffffffffffff16604091909101819052600b805473ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000909202919091179055565b60006fffffffffffffffffffffffffffffffff841615612fdb57612fd6612fd1856fffffffffffffffffffffffffffffffff1687606001518686613bc1565b613bfd565b612fde565b60005b90505b949350505050565b612ff38484613459565b6040516fffffffffffffffffffffffffffffffff8416815273ffffffffffffffffffffffffffffffffffffffff80831691908416907fb81daa514d39133aa6960d430d2f15f0c150cb0e4d51c8ff506080c34985f6009060200160405180910390a36130a973ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f1683306fffffffffffffffffffffffffffffffff871661367d565b6005546006546040517f5ceae9c400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f811660048301526fffffffffffffffffffffffffffffffff871660248301529182166044820152911690635ceae9c4906064016123c4565b60035460009060ff1615613175575060005473ffffffffffffffffffffffffffffffffffffffff838116911614613201565b60015473ffffffffffffffffffffffffffffffffffffffff848116911614806131f0575073ffffffffffffffffffffffffffffffffffffffff831660009081526002602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff165b156131fd57506001613201565b5060005b92915050565b604080516080810182526000808252918101829052606081019190915282546fffffffffffffffffffffffffffffffff80821660208401526001808601546132679386937001000000000000000000000000000000009091041691612f92565b6fffffffffffffffffffffffffffffffff908116606083015260808301516020830151670de0b6b3a76400009261329f929116614f64565b6132a99190614faa565b604082015260208101516132be90839061385d565b60608201516fffffffffffffffffffffffffffffffff918216911611815292915050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015613358573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061337c9190614e3a565b9050611072847f095ea7b300000000000000000000000000000000000000000000000000000000856133ae8686614e53565b60405173ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613c4c565b806fffffffffffffffffffffffffffffffff16600003613477575050565b600082602001516fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff16116134b457818360200151036134b7565b60005b6fffffffffffffffffffffffffffffffff1660208401819052600b80547fffffffffffffffffffffffff00000000000000000000000000000000ffffffff166401000000009092029190911790555061198182612e4a565b6040805161012081018252600060c0820181815260e083018290526101008301829052825260208201819052918101829052606081018290526080810182905260a081019190915261356081613951565b5090565b60006135708383613207565b805190915015611e36576020810151604080830151606084015191517f049f16120000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff9384166004820152602481019190915291166044820152606401610e0e565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611e369084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016133d7565b60008160a001516000146136755761367082602001516fffffffffffffffffffffffffffffffff16670de0b6b3a76400008460a00151613d5b565b613201565b600092915050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526110729085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016133d7565b80158061377b57506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015613755573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137799190614e3a565b155b613807576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610e0e565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611e369084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016133d7565b60006138ad612fd18460800151846fffffffffffffffffffffffffffffffff166138879190614f64565b8551516bffffffffffffffffffffffff166ec097ce7bc90715b34b9f1000000000613d5b565b9392505050565b600081156138fc576138f78460800151846138cf9190614f64565b8551516bffffffffffffffffffffffff166138f2670de0b6b3a764000086614f64565b613d5b565b612fe1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff949350505050565b600082156138fc576138f7826ec097ce7bc90715b34b9f10000000008660800151866138f29190614f64565b60408051606080820183526009546bffffffffffffffffffffffff808216845273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009092048216602080860191909152600a5460ff16151585870152938652600c5492860192909252600b546fffffffffffffffffffffffffffffffff6401000000008204169386019390935274010000000000000000000000000000000000000000909204168383015260055460065492517fdd07270400000000000000000000000000000000000000000000000000000000815292821660048401527f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f82166024840152600092911690819063dd07270490604401602060405180830381865afa158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614e3a565b8360a00181815250508073ffffffffffffffffffffffffffffffffffffffff16639ed384f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015613afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b229190614f36565b6bffffffffffffffffffffffff16608084015250600b5463ffffffff908116420390811615613bbb57600191506000613b748263ffffffff1685604001518660600151613e669092919063ffffffff16565b9050613b9e612fd18286602001516fffffffffffffffffffffffffffffffff168760600151613d5b565b6fffffffffffffffffffffffffffffffff16602085015260608401525b50919050565b6000613bce858585613d5b565b9050818015613bed575060008380613be857613be8614f7b565b858709115b15612fe157612fde600182614e53565b60006fffffffffffffffffffffffffffffffff821115613560576040517fe0fb6a7c00000000000000000000000000000000000000000000000000000000815260048101839052602401610e0e565b6000613cae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613eb19092919063ffffffff16565b9050805160001480613ccf575080806020019051810190613ccf9190614fe5565b611e36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610e0e565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003613db357838281613da957613da9614f7b565b04925050506138ad565b838110613dfd576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610e0e565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000806301e13380613e866bffffffffffffffffffffffff851686614f64565b613e909190614faa565b9050613ea8612e47613ea183613ec0565b8790613f27565b95945050505050565b6060612fe18484600085613f36565b600081680736ea4425c11ac630811115613f09576040517f1af63aca00000000000000000000000000000000000000000000000000000000815260048101849052602401610e0e565b6714057b7ef767814f8102612fe1670de0b6b3a7640000820461404f565b60006138ad612e4784846140be565b606082471015613fc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610e0e565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613ff19190615026565b60006040518083038185875af1925050503d806000811461402e576040519150601f19603f3d011682016040523d82523d6000602084013e614033565b606091505b5091509150614044878383876141c5565b979650505050505050565b600081680a688906bd8affffff811115614098576040517fb3b6ba1f00000000000000000000000000000000000000000000000000000000815260048101849052602401610e0e565b60006140b0670de0b6b3a7640000604084901b614faa565b9050612fe1612e4782614265565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848609848602925082811083820303915050806000036141105750670de0b6b3a764000090049050613201565b670de0b6b3a7640000811061415b576040517f5173648d0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604401610e0e565b6000670de0b6b3a7640000858709620400008185030493109091037d40000000000000000000000000000000000000000000000000000000000002919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b6060831561425b5782516000036142545773ffffffffffffffffffffffffffffffffffffffff85163b614254576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e0e565b5081612fe1565b612fe183836149d9565b7780000000000000000000000000000000000000000000000067ff00000000000000821615614386576780000000000000008216156142ad5768016a09e667f3bcc9090260401c5b6740000000000000008216156142cc576801306fe0a31b7152df0260401c5b6720000000000000008216156142eb576801172b83c7d517adce0260401c5b67100000000000000082161561430a5768010b5586cf9890f62a0260401c5b670800000000000000821615614329576801059b0d31585743ae0260401c5b67040000000000000082161561434857680102c9a3e778060ee70260401c5b6702000000000000008216156143675768010163da9fb33356d80260401c5b67010000000000000082161561438657680100b1afa5abcbed610260401c5b66ff0000000000008216156144855766800000000000008216156143b35768010058c86da1c09ea20260401c5b66400000000000008216156143d1576801002c605e2e8cec500260401c5b66200000000000008216156143ef57680100162f3904051fa10260401c5b661000000000000082161561440d576801000b175effdc76ba0260401c5b660800000000000082161561442b57680100058ba01fb9f96d0260401c5b66040000000000008216156144495768010002c5cc37da94920260401c5b6602000000000000821615614467576801000162e525ee05470260401c5b66010000000000008216156144855768010000b17255775c040260401c5b65ff000000000082161561457b57658000000000008216156144b0576801000058b91b5bc9ae0260401c5b654000000000008216156144cd57680100002c5c89d5ec6d0260401c5b652000000000008216156144ea5768010000162e43f4f8310260401c5b6510000000000082161561450757680100000b1721bcfc9a0260401c5b650800000000008216156145245768010000058b90cf1e6e0260401c5b65040000000000821615614541576801000002c5c863b73f0260401c5b6502000000000082161561455e57680100000162e430e5a20260401c5b6501000000000082161561457b576801000000b1721835510260401c5b64ff00000000821615614668576480000000008216156145a457680100000058b90c0b490260401c5b6440000000008216156145c05768010000002c5c8601cc0260401c5b6420000000008216156145dc576801000000162e42fff00260401c5b6410000000008216156145f85768010000000b17217fbb0260401c5b640800000000821615614614576801000000058b90bfce0260401c5b64040000000082161561463057680100000002c5c85fe30260401c5b64020000000082161561464c5768010000000162e42ff10260401c5b64010000000082161561466857680100000000b17217f80260401c5b63ff00000082161561474c57638000000082161561468f5768010000000058b90bfc0260401c5b63400000008216156146aa576801000000002c5c85fe0260401c5b63200000008216156146c557680100000000162e42ff0260401c5b63100000008216156146e0576801000000000b17217f0260401c5b63080000008216156146fb57680100000000058b90c00260401c5b63040000008216156147165768010000000002c5c8600260401c5b6302000000821615614731576801000000000162e4300260401c5b630100000082161561474c5768010000000000b172180260401c5b62ff00008216156148275762800000821615614771576801000000000058b90c0260401c5b6240000082161561478b57680100000000002c5c860260401c5b622000008216156147a55768010000000000162e430260401c5b621000008216156147bf57680100000000000b17210260401c5b620800008216156147d95768010000000000058b910260401c5b620400008216156147f3576801000000000002c5c80260401c5b6202000082161561480d57680100000000000162e40260401c5b62010000821615614827576801000000000000b1720260401c5b61ff008216156148f95761800082161561484a57680100000000000058b90260401c5b6140008216156148635768010000000000002c5d0260401c5b61200082161561487c576801000000000000162e0260401c5b6110008216156148955768010000000000000b170260401c5b6108008216156148ae576801000000000000058c0260401c5b6104008216156148c757680100000000000002c60260401c5b6102008216156148e057680100000000000001630260401c5b6101008216156148f957680100000000000000b10260401c5b60ff8216156149c257608082161561491a57680100000000000000590260401c5b6040821615614932576801000000000000002c0260401c5b602082161561494a57680100000000000000160260401c5b6010821615614962576801000000000000000b0260401c5b600882161561497a57680100000000000000060260401c5b600482161561499257680100000000000000030260401c5b60028216156149aa57680100000000000000010260401c5b60018216156149c257680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b8151156149e95781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0e9190615042565b8015158114614a2b57600080fd5b50565b600060208284031215614a4057600080fd5b81356138ad81614a1d565b73ffffffffffffffffffffffffffffffffffffffff81168114614a2b57600080fd5b600060208284031215614a7f57600080fd5b81356138ad81614a4b565b80356fffffffffffffffffffffffffffffffff81168114614aaa57600080fd5b919050565b60008060408385031215614ac257600080fd5b614acb83614a8a565b91506020830135614adb81614a4b565b809150509250929050565b600060208284031215614af857600080fd5b6138ad82614a8a565b60008060208385031215614b1457600080fd5b823567ffffffffffffffff80821115614b2c57600080fd5b818501915085601f830112614b4057600080fd5b813581811115614b4f57600080fd5b8660208260051b8501011115614b6457600080fd5b60209290920196919550909350505050565b600060208284031215614b8857600080fd5b5035919050565b600080600060608486031215614ba457600080fd5b8335614baf81614a4b565b92506020840135614bbf81614a4b565b929592945050506040919091013590565b600080600060408486031215614be557600080fd5b8335614bf081614a4b565b9250602084013567ffffffffffffffff80821115614c0d57600080fd5b818601915086601f830112614c2157600080fd5b813581811115614c3057600080fd5b8760208260061b8501011115614c4557600080fd5b6020830194508093505050509250925092565b602080825282518282018190526000919060409081850190868401855b82811015614cc6578151805115158552868101516fffffffffffffffffffffffffffffffff908116888701528682015187870152606091820151169085015260809093019290850190600101614c75565b5091979650505050505050565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614aaa57600080fd5b60008060408385031215614d1657600080fd5b8235614d2181614a4b565b9150614d2f60208401614cd3565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff818116838216019080821115614dbf57614dbf614d67565b5092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614df757614df7614d67565b5060010190565b8181038181111561320157613201614d67565b6fffffffffffffffffffffffffffffffff828116828216039080821115614dbf57614dbf614d67565b600060208284031215614e4c57600080fd5b5051919050565b8082018082111561320157613201614d67565b600060208284031215614e7857600080fd5b81516138ad81614a4b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060408284031215614ec457600080fd5b6040516040810181811067ffffffffffffffff82111715614f0e577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614f1a83614cd3565b81526020830135614f2a81614a1d565b60208201529392505050565b600060208284031215614f4857600080fd5b81516bffffffffffffffffffffffff811681146138ad57600080fd5b808202811582820484141761320157613201614d67565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614fe0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208284031215614ff757600080fd5b81516138ad81614a1d565b60005b8381101561501d578181015183820152602001615005565b50506000910152565b60008251615038818460208701615002565b9190910192915050565b6020815260008251806020840152615061816040850160208701615002565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea26469706673582212208839514266137ae35124e7fd5a779701e64e0b129ac9b43120bc98a16e91694d64736f6c63430008130033

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

0000000000000000000000009f90430179d9b67341bfa50559bc7b8e35629f1b000000000000000000000000b1e8ab0a81aadf632a653e46caeda1593d71d14300000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a480000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b70000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000009498ab765beed8292938937079ac56080b8a179d

-----Decoded View---------------
Arg [0] : _initialRescuer (address): 0x9f90430179D9b67341BFa50559bc7B8E35629f1b
Arg [1] : _initialExecutor (address): 0xb1E8AB0a81AaDf632a653E46Caeda1593d71d143
Arg [2] : _circuitBreakerProxy (address): 0x87B8D213177FB132e508b5d7018f7b590e00a480
Arg [3] : _templeToken (address): 0x470EBf5f030Ed85Fc1ed4C2d36B9DD02e77CF1b7
Arg [4] : _daiToken (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F
Arg [5] : _maxLtvRatio (uint256): 800000000000000000
Arg [6] : _interestRateModel (address): 0x9498ab765BeeD8292938937079Ac56080B8a179d

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000009f90430179d9b67341bfa50559bc7b8e35629f1b
Arg [1] : 000000000000000000000000b1e8ab0a81aadf632a653e46caeda1593d71d143
Arg [2] : 00000000000000000000000087b8d213177fb132e508b5d7018f7b590e00a480
Arg [3] : 000000000000000000000000470ebf5f030ed85fc1ed4c2d36b9dd02e77cf1b7
Arg [4] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [5] : 0000000000000000000000000000000000000000000000000b1a2bc2ec500000
Arg [6] : 0000000000000000000000009498ab765beed8292938937079ac56080b8a179d


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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