Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 172 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Borrow | 20959012 | 4 days ago | IN | 0 ETH | 0.01078259 | ||||
Repay All | 20956358 | 4 days ago | IN | 0 ETH | 0.00356733 | ||||
Add Collateral | 20945119 | 6 days ago | IN | 0 ETH | 0.00060144 | ||||
Borrow | 20913180 | 10 days ago | IN | 0 ETH | 0.00257399 | ||||
Add Collateral | 20913175 | 10 days ago | IN | 0 ETH | 0.00058904 | ||||
Add Collateral | 20912739 | 10 days ago | IN | 0 ETH | 0.00064866 | ||||
Borrow | 20893126 | 13 days ago | IN | 0 ETH | 0.00477357 | ||||
Add Collateral | 20893117 | 13 days ago | IN | 0 ETH | 0.00088685 | ||||
Add Collateral | 20893079 | 13 days ago | IN | 0 ETH | 0.00096961 | ||||
Repay | 20886311 | 14 days ago | IN | 0 ETH | 0.00393878 | ||||
Repay | 20886282 | 14 days ago | IN | 0 ETH | 0.00447938 | ||||
Repay All | 20771019 | 30 days ago | IN | 0 ETH | 0.00562004 | ||||
Borrow | 20758658 | 32 days ago | IN | 0 ETH | 0.00147471 | ||||
Borrow | 20756398 | 32 days ago | IN | 0 ETH | 0.00060861 | ||||
Borrow | 20744334 | 34 days ago | IN | 0 ETH | 0.00268373 | ||||
Borrow | 20742050 | 34 days ago | IN | 0 ETH | 0.00162264 | ||||
Repay | 20672952 | 44 days ago | IN | 0 ETH | 0.00107947 | ||||
Remove Collatera... | 20660417 | 45 days ago | IN | 0 ETH | 0.00015203 | ||||
Repay All | 20626095 | 50 days ago | IN | 0 ETH | 0.00099738 | ||||
Borrow | 20622752 | 51 days ago | IN | 0 ETH | 0.00493125 | ||||
Repay All | 20547768 | 61 days ago | IN | 0 ETH | 0.00083794 | ||||
Repay | 20535876 | 63 days ago | IN | 0 ETH | 0.00314556 | ||||
Borrow | 20535221 | 63 days ago | IN | 0 ETH | 0.0047273 | ||||
Borrow | 20521722 | 65 days ago | IN | 0 ETH | 0.00036599 | ||||
Add Collateral | 20521718 | 65 days ago | IN | 0 ETH | 0.00008518 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
TempleLineOfCredit
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
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 ); } }
// 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); }
// 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); }
// 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); }
// 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)); } }
// 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); } } }
// 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; } } }
// 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); }
// 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;
// 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);
// 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;
// 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); }
// 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);
// 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);
// 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()); }
// 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)); } }
// 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;
// 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); }
// 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);
// SPDX-License-Identifier: MIT pragma solidity >=0.8.19; import { UD2x18 } from "./ValueType.sol"; /// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18. error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x); /// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40. error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);
// SPDX-License-Identifier: MIT pragma solidity >=0.8.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;
// 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";
// 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); }
// 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);
// 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); } }
// 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);
// 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()); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.19; import "../Common.sol" as Common; import "./Errors.sol" as Errors; import { wrap } from "./Casting.sol"; import { uEXP_MAX_INPUT, uEXP2_MAX_INPUT, uHALF_UNIT, uLOG2_10, uLOG2_E, uMAX_UD60x18, uMAX_WHOLE_UD60x18, UNIT, uUNIT, uUNIT_SQUARED, ZERO } from "./Constants.sol"; import { UD60x18 } from "./ValueType.sol"; /*////////////////////////////////////////////////////////////////////////// MATHEMATICAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Calculates the arithmetic average of x and y using the following formula: /// /// $$ /// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2) /// $$ // /// In English, this is what this formula does: /// /// 1. AND x and y. /// 2. Calculate half of XOR x and y. /// 3. Add the two results together. /// /// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here: /// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223 /// /// @dev Notes: /// - The result is rounded toward zero. /// /// @param x The first operand as a UD60x18 number. /// @param y The second operand as a UD60x18 number. /// @return result The arithmetic average as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); uint256 yUint = y.unwrap(); unchecked { result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1)); } } /// @notice Yields the smallest whole number greater than or equal to x. /// /// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional /// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// /// Requirements: /// - x must be less than or equal to `MAX_WHOLE_UD60x18`. /// /// @param x The UD60x18 number to ceil. /// @param result The smallest whole number greater than or equal to x, as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function ceil(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); if (xUint > uMAX_WHOLE_UD60x18) { revert Errors.PRBMath_UD60x18_Ceil_Overflow(x); } assembly ("memory-safe") { // Equivalent to `x % UNIT`. let remainder := mod(x, uUNIT) // Equivalent to `UNIT - remainder`. let delta := sub(uUNIT, remainder) // Equivalent to `x + remainder > 0 ? delta : 0`. result := add(x, mul(delta, gt(remainder, 0))) } } /// @notice Divides two UD60x18 numbers, returning a new UD60x18 number. /// /// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division. /// /// Notes: /// - Refer to the notes in {Common.mulDiv}. /// /// Requirements: /// - Refer to the requirements in {Common.mulDiv}. /// /// @param x The numerator as a UD60x18 number. /// @param y The denominator as a UD60x18 number. /// @param result The quotient as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) { result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap())); } /// @notice Calculates the natural exponent of x using the following formula: /// /// $$ /// e^x = 2^{x * log_2{e}} /// $$ /// /// @dev Requirements: /// - x must be less than 133_084258667509499441. /// /// @param x The exponent as a UD60x18 number. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function exp(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); // This check prevents values greater than 192e18 from being passed to {exp2}. if (xUint > uEXP_MAX_INPUT) { revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x); } unchecked { // Inline the fixed-point multiplication to save gas. uint256 doubleUnitProduct = xUint * uLOG2_E; result = exp2(wrap(doubleUnitProduct / uUNIT)); } } /// @notice Calculates the binary exponent of x using the binary fraction method. /// /// @dev See https://ethereum.stackexchange.com/q/79903/24693 /// /// Requirements: /// - x must be less than 192e18. /// - The result must fit in UD60x18. /// /// @param x The exponent as a UD60x18 number. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function exp2(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format. if (xUint > uEXP2_MAX_INPUT) { revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x); } // Convert x to the 192.64-bit fixed-point format. uint256 x_192x64 = (xUint << 64) / uUNIT; // Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation. result = wrap(Common.exp2(x_192x64)); } /// @notice Yields the greatest whole number less than or equal to x. /// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts. /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// @param x The UD60x18 number to floor. /// @param result The greatest whole number less than or equal to x, as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function floor(UD60x18 x) pure returns (UD60x18 result) { assembly ("memory-safe") { // Equivalent to `x % UNIT`. let remainder := mod(x, uUNIT) // Equivalent to `x - remainder > 0 ? remainder : 0)`. result := sub(x, mul(remainder, gt(remainder, 0))) } } /// @notice Yields the excess beyond the floor of x using the odd function definition. /// @dev See https://en.wikipedia.org/wiki/Fractional_part. /// @param x The UD60x18 number to get the fractional part of. /// @param result The fractional part of x as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function frac(UD60x18 x) pure returns (UD60x18 result) { assembly ("memory-safe") { result := mod(x, uUNIT) } } /// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down. /// /// @dev Requirements: /// - x * y must fit in UD60x18. /// /// @param x The first operand as a UD60x18 number. /// @param y The second operand as a UD60x18 number. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); uint256 yUint = y.unwrap(); if (xUint == 0 || yUint == 0) { return ZERO; } unchecked { // Checking for overflow this way is faster than letting Solidity do it. uint256 xyUint = xUint * yUint; if (xyUint / xUint != yUint) { revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y); } // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT` // during multiplication. See the comments in {Common.sqrt}. result = wrap(Common.sqrt(xyUint)); } } /// @notice Calculates the inverse of x. /// /// @dev Notes: /// - The result is rounded toward zero. /// /// Requirements: /// - x must not be zero. /// /// @param x The UD60x18 number for which to calculate the inverse. /// @return result The inverse as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function inv(UD60x18 x) pure returns (UD60x18 result) { unchecked { result = wrap(uUNIT_SQUARED / x.unwrap()); } } /// @notice Calculates the natural logarithm of x using the following formula: /// /// $$ /// ln{x} = log_2{x} / log_2{e} /// $$ /// /// @dev Notes: /// - Refer to the notes in {log2}. /// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`. /// /// Requirements: /// - Refer to the requirements in {log2}. /// /// @param x The UD60x18 number for which to calculate the natural logarithm. /// @return result The natural logarithm as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function ln(UD60x18 x) pure returns (UD60x18 result) { unchecked { // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that // {log2} can return is ~196_205294292027477728. result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E); } } /// @notice Calculates the common logarithm of x using the following formula: /// /// $$ /// log_{10}{x} = log_2{x} / log_2{10} /// $$ /// /// However, if x is an exact power of ten, a hard coded value is returned. /// /// @dev Notes: /// - Refer to the notes in {log2}. /// /// Requirements: /// - Refer to the requirements in {log2}. /// /// @param x The UD60x18 number for which to calculate the common logarithm. /// @return result The common logarithm as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function log10(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); if (xUint < uUNIT) { revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x); } // Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}. // prettier-ignore assembly ("memory-safe") { switch x case 1 { result := mul(uUNIT, sub(0, 18)) } case 10 { result := mul(uUNIT, sub(1, 18)) } case 100 { result := mul(uUNIT, sub(2, 18)) } case 1000 { result := mul(uUNIT, sub(3, 18)) } case 10000 { result := mul(uUNIT, sub(4, 18)) } case 100000 { result := mul(uUNIT, sub(5, 18)) } case 1000000 { result := mul(uUNIT, sub(6, 18)) } case 10000000 { result := mul(uUNIT, sub(7, 18)) } case 100000000 { result := mul(uUNIT, sub(8, 18)) } case 1000000000 { result := mul(uUNIT, sub(9, 18)) } case 10000000000 { result := mul(uUNIT, sub(10, 18)) } case 100000000000 { result := mul(uUNIT, sub(11, 18)) } case 1000000000000 { result := mul(uUNIT, sub(12, 18)) } case 10000000000000 { result := mul(uUNIT, sub(13, 18)) } case 100000000000000 { result := mul(uUNIT, sub(14, 18)) } case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) } case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) } case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) } case 1000000000000000000 { result := 0 } case 10000000000000000000 { result := uUNIT } case 100000000000000000000 { result := mul(uUNIT, 2) } case 1000000000000000000000 { result := mul(uUNIT, 3) } case 10000000000000000000000 { result := mul(uUNIT, 4) } case 100000000000000000000000 { result := mul(uUNIT, 5) } case 1000000000000000000000000 { result := mul(uUNIT, 6) } case 10000000000000000000000000 { result := mul(uUNIT, 7) } case 100000000000000000000000000 { result := mul(uUNIT, 8) } case 1000000000000000000000000000 { result := mul(uUNIT, 9) } case 10000000000000000000000000000 { result := mul(uUNIT, 10) } case 100000000000000000000000000000 { result := mul(uUNIT, 11) } case 1000000000000000000000000000000 { result := mul(uUNIT, 12) } case 10000000000000000000000000000000 { result := mul(uUNIT, 13) } case 100000000000000000000000000000000 { result := mul(uUNIT, 14) } case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) } case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) } case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) } case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) } case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) } case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) } case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) } case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) } case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) } case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) } case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) } case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) } case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) } case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) } case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) } case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) } case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) } case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) } case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) } case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) } case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) } case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) } case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) } case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) } case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) } case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) } case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) } case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) } case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) } case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) } case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) } case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) } case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) } case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) } case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) } case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) } case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) } case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) } case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) } case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) } case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) } case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) } case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) } case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) } case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) } default { result := uMAX_UD60x18 } } if (result.unwrap() == uMAX_UD60x18) { unchecked { // Inline the fixed-point division to save gas. result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10); } } } /// @notice Calculates the binary logarithm of x using the iterative approximation algorithm: /// /// $$ /// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2) /// $$ /// /// For $0 \leq x \lt 1$, the input is inverted: /// /// $$ /// log_2{x} = -log_2{\frac{1}{x}} /// $$ /// /// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation /// /// Notes: /// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal. /// /// Requirements: /// - x must be greater than zero. /// /// @param x The UD60x18 number for which to calculate the binary logarithm. /// @return result The binary logarithm as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function log2(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); if (xUint < uUNIT) { revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x); } unchecked { // Calculate the integer part of the logarithm. uint256 n = Common.msb(xUint / uUNIT); // This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n // n is at most 255 and UNIT is 1e18. uint256 resultUint = n * uUNIT; // Calculate $y = x * 2^{-n}$. uint256 y = xUint >> n; // If y is the unit number, the fractional part is zero. if (y == uUNIT) { return wrap(resultUint); } // Calculate the fractional part via the iterative approximation. // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient. uint256 DOUBLE_UNIT = 2e18; for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) { y = (y * y) / uUNIT; // Is y^2 >= 2e18 and so in the range [2e18, 4e18)? if (y >= DOUBLE_UNIT) { // Add the 2^{-m} factor to the logarithm. resultUint += delta; // Halve y, which corresponds to z/2 in the Wikipedia article. y >>= 1; } } result = wrap(resultUint); } } /// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number. /// /// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division. /// /// Notes: /// - Refer to the notes in {Common.mulDiv}. /// /// Requirements: /// - Refer to the requirements in {Common.mulDiv}. /// /// @dev See the documentation in {Common.mulDiv18}. /// @param x The multiplicand as a UD60x18 number. /// @param y The multiplier as a UD60x18 number. /// @return result The product as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) { result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap())); } /// @notice Raises x to the power of y. /// /// For $1 \leq x \leq \infty$, the following standard formula is used: /// /// $$ /// x^y = 2^{log_2{x} * y} /// $$ /// /// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used: /// /// $$ /// i = \frac{1}{x} /// w = 2^{log_2{i} * y} /// x^y = \frac{1}{w} /// $$ /// /// @dev Notes: /// - Refer to the notes in {log2} and {mul}. /// - Returns `UNIT` for 0^0. /// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative. /// /// Requirements: /// - Refer to the requirements in {exp2}, {log2}, and {mul}. /// /// @param x The base as a UD60x18 number. /// @param y The exponent as a UD60x18 number. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); uint256 yUint = y.unwrap(); // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero. if (xUint == 0) { return yUint == 0 ? UNIT : ZERO; } // If x is `UNIT`, the result is always `UNIT`. else if (xUint == uUNIT) { return UNIT; } // If y is zero, the result is always `UNIT`. if (yUint == 0) { return UNIT; } // If y is `UNIT`, the result is always x. else if (yUint == uUNIT) { return x; } // If x is greater than `UNIT`, use the standard formula. if (xUint > uUNIT) { result = exp2(mul(log2(x), y)); } // Conversely, if x is less than `UNIT`, use the equivalent formula. else { UD60x18 i = wrap(uUNIT_SQUARED / xUint); UD60x18 w = exp2(mul(log2(i), y)); result = wrap(uUNIT_SQUARED / w.unwrap()); } } /// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known /// algorithm "exponentiation by squaring". /// /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring. /// /// Notes: /// - Refer to the notes in {Common.mulDiv18}. /// - Returns `UNIT` for 0^0. /// /// Requirements: /// - The result must fit in UD60x18. /// /// @param x The base as a UD60x18 number. /// @param y The exponent as a uint256. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) { // Calculate the first iteration of the loop in advance. uint256 xUint = x.unwrap(); uint256 resultUint = y & 1 > 0 ? xUint : uUNIT; // Equivalent to `for(y /= 2; y > 0; y /= 2)`. for (y >>= 1; y > 0; y >>= 1) { xUint = Common.mulDiv18(xUint, xUint); // Equivalent to `y % 2 == 1`. if (y & 1 > 0) { resultUint = Common.mulDiv18(resultUint, xUint); } } result = wrap(resultUint); } /// @notice Calculates the square root of x using the Babylonian method. /// /// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. /// /// Notes: /// - The result is rounded toward zero. /// /// Requirements: /// - x must be less than `MAX_UD60x18 / UNIT`. /// /// @param x The UD60x18 number for which to calculate the square root. /// @return result The result as a UD60x18 number. /// @custom:smtchecker abstract-function-nondet function sqrt(UD60x18 x) pure returns (UD60x18 result) { uint256 xUint = x.unwrap(); unchecked { if (xUint > uMAX_UD60x18 / uUNIT) { revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x); } // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers. // In this case, the two numbers are both the square root. result = wrap(Common.sqrt(xUint * uUNIT)); } }
// SPDX-License-Identifier: 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;
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); }
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); } }
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; }
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; }
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); }
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); }
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); }
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); }
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); }
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); }
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; }
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; }
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); }
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; } }
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); }
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(); _; } }
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(); } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $1.72 | 14,003,264.859 | $24,085,615.56 |
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.