Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 164 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Redeem | 22821741 | 102 days ago | IN | 0 ETH | 0.00041039 | ||||
Redeem | 22670452 | 123 days ago | IN | 0 ETH | 0.00074638 | ||||
Redeem | 22609524 | 132 days ago | IN | 0 ETH | 0.0005097 | ||||
Redeem | 22444486 | 155 days ago | IN | 0 ETH | 0.00145925 | ||||
Redeem | 22410516 | 159 days ago | IN | 0 ETH | 0.00012164 | ||||
Redeem | 22354394 | 167 days ago | IN | 0 ETH | 0.00037104 | ||||
Redeem | 22354391 | 167 days ago | IN | 0 ETH | 0.00038257 | ||||
Redeem | 22335143 | 170 days ago | IN | 0 ETH | 0.00030821 | ||||
Redeem | 22332068 | 170 days ago | IN | 0 ETH | 0.00110691 | ||||
Redeem | 22328619 | 171 days ago | IN | 0 ETH | 0.00027458 | ||||
Redeem | 22319117 | 172 days ago | IN | 0 ETH | 0.00020935 | ||||
Redeem | 22289784 | 176 days ago | IN | 0 ETH | 0.00031829 | ||||
Redeem | 22275570 | 178 days ago | IN | 0 ETH | 0.00051816 | ||||
Redeem | 22246489 | 182 days ago | IN | 0 ETH | 0.00087655 | ||||
Redeem | 22236122 | 184 days ago | IN | 0 ETH | 0.00028192 | ||||
Redeem | 22222770 | 186 days ago | IN | 0 ETH | 0.00014967 | ||||
Redeem | 22214853 | 187 days ago | IN | 0 ETH | 0.00044731 | ||||
Redeem | 22198214 | 189 days ago | IN | 0 ETH | 0.00013727 | ||||
Redeem | 22190613 | 190 days ago | IN | 0 ETH | 0.00031232 | ||||
Redeem | 22187867 | 191 days ago | IN | 0 ETH | 0.00027287 | ||||
Redeem | 22186689 | 191 days ago | IN | 0 ETH | 0.00028529 | ||||
Redeem | 22173448 | 193 days ago | IN | 0 ETH | 0.00028927 | ||||
Redeem | 22172876 | 193 days ago | IN | 0 ETH | 0.00039299 | ||||
Redeem | 22166149 | 194 days ago | IN | 0 ETH | 0.00035247 | ||||
Redeem | 22156334 | 195 days ago | IN | 0 ETH | 0.00026955 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Strategy
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {BaseStrategy, ERC20} from "@tokenized-strategy/BaseStrategy.sol"; import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {ITermRepoToken} from "./interfaces/term/ITermRepoToken.sol"; import {ITermRepoServicer} from "./interfaces/term/ITermRepoServicer.sol"; import {ITermController} from "./interfaces/term/ITermController.sol"; import {ITermVaultEvents} from "./interfaces/term/ITermVaultEvents.sol"; import {ITermAuctionOfferLocker} from "./interfaces/term/ITermAuctionOfferLocker.sol"; import {ITermDiscountRateAdapter} from "./interfaces/term/ITermDiscountRateAdapter.sol"; import {ITermAuction} from "./interfaces/term/ITermAuction.sol"; import {RepoTokenList, RepoTokenListData} from "./RepoTokenList.sol"; import {TermAuctionList, TermAuctionListData, PendingOffer} from "./TermAuctionList.sol"; import {RepoTokenUtils} from "./RepoTokenUtils.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; // Import interfaces for many popular DeFi projects, or add your own! //import "../interfaces/<protocol>/<Interface>.sol"; /** * The `TokenizedStrategy` variable can be used to retrieve the strategies * specific storage data your contract. * * i.e. uint256 totalAssets = TokenizedStrategy.totalAssets() * * This can not be used for write functions. Any TokenizedStrategy * variables that need to be updated post deployment will need to * come from an external call from the strategies specific `management`. */ // NOTE: To implement permissioned functions you can use the onlyManagement, onlyEmergencyAuthorized and onlyKeepers modifiers contract Strategy is BaseStrategy, Pausable, AccessControl { using SafeERC20 for IERC20; using RepoTokenList for RepoTokenListData; using TermAuctionList for TermAuctionListData; /** * @notice Constructor to initialize the Strategy contract * @param _asset The address of the asset * @param _yearnVault The address of the Yearn vault * @param _discountRateAdapter The address of the discount rate adapter * @param _eventEmitter The address of the event emitter * @param _governorAddress The address of the governor * @param _termController The address of the term controller * @param _repoTokenConcentrationLimit The concentration limit for repoTokens * @param _timeToMaturityThreshold The time to maturity threshold * @param _requiredReserveRatio The required reserve ratio * @param _discountRateMarkup The discount rate markup */ struct StrategyParams { address _asset; address _yearnVault; address _discountRateAdapter; address _eventEmitter; address _governorAddress; address _termController; uint256 _repoTokenConcentrationLimit; uint256 _timeToMaturityThreshold; uint256 _requiredReserveRatio; uint256 _discountRateMarkup; } struct StrategyState { address assetVault; address eventEmitter; address governorAddress; ITermController prevTermController; ITermController currTermController; ITermDiscountRateAdapter discountRateAdapter; uint256 timeToMaturityThreshold; uint256 requiredReserveRatio; uint256 discountRateMarkup; uint256 repoTokenConcentrationLimit; } // Custom errors error InvalidTermAuction(address auction); error TimeToMaturityAboveThreshold(); error BalanceBelowRequiredReserveRatio(); error InsufficientLiquidBalance(uint256 have, uint256 want); error RepoTokenConcentrationTooHigh(address repoToken); error RepoTokenBlacklisted(address repoToken); error DepositPaused(); error AuctionNotOpen(); error ZeroPurchaseTokenAmount(); error OfferNotFound(); bytes32 internal constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); // Immutable state variables ITermVaultEvents internal immutable TERM_VAULT_EVENT_EMITTER; uint256 internal immutable PURCHASE_TOKEN_PRECISION; IERC4626 internal immutable YEARN_VAULT; /// @notice State variables bool internal depositLock; address internal pendingGovernor; RepoTokenListData internal repoTokenListData; TermAuctionListData internal termAuctionListData; string internal tokenSymbol; StrategyState public strategyState; mapping(address => bool) public repoTokenBlacklist; modifier notBlacklisted(address repoToken) { if (repoTokenBlacklist[repoToken]) { revert RepoTokenBlacklisted(repoToken); } _; } /*////////////////////////////////////////////////////////////// MANAGEMENT FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Pause the contract */ function pauseDeposit() external onlyRole(GOVERNOR_ROLE) { depositLock = true; TERM_VAULT_EVENT_EMITTER.emitDepositPaused(); } /** * @notice Unpause the contract */ function unpauseDeposit() external onlyRole(GOVERNOR_ROLE) { depositLock = false; TERM_VAULT_EVENT_EMITTER.emitDepositUnpaused(); } /** * @notice Pause the contract */ function pauseStrategy() external onlyRole(GOVERNOR_ROLE) { _pause(); depositLock = true; //TERM_VAULT_EVENT_EMITTER.emitStrategyPaused(); } /** * @notice Unpause the contract */ function unpauseStrategy() external onlyRole(GOVERNOR_ROLE) { _unpause(); depositLock = false; //TERM_VAULT_EVENT_EMITTER.emitStrategyUnpaused(); } function setPendingGovernor( address newGovernor ) external onlyRole(GOVERNOR_ROLE) { require(newGovernor != address(0)); pendingGovernor = newGovernor; } function acceptGovernor() external { require(msg.sender == pendingGovernor, "!pendingGovernor"); _revokeRole(GOVERNOR_ROLE, strategyState.governorAddress); _grantRole(GOVERNOR_ROLE, pendingGovernor); strategyState.governorAddress = pendingGovernor; TERM_VAULT_EVENT_EMITTER.emitNewGovernor(pendingGovernor); pendingGovernor = address(0); } /** * @notice Set the term controller * @param newTermControllerAddr The address of the new term controller */ function setTermController( address newTermControllerAddr ) external onlyRole(GOVERNOR_ROLE) { require(newTermControllerAddr != address(0)); require( ITermController(newTermControllerAddr) .getProtocolReserveAddress() != address(0) ); address currentIteration = repoTokenListData.head; while (currentIteration != address(0)) { if (!_isTermDeployed(currentIteration)) { revert RepoTokenList.InvalidRepoToken(currentIteration); } currentIteration = repoTokenListData.nodes[currentIteration].next; } address current = address(strategyState.currTermController); TERM_VAULT_EVENT_EMITTER.emitTermControllerUpdated( current, newTermControllerAddr ); strategyState.prevTermController = ITermController(current); strategyState.currTermController = ITermController( newTermControllerAddr ); } /** * @notice Set the discount rate adapter used to price repoTokens * @param newAdapter The address of the new discount rate adapter */ function setDiscountRateAdapter( address newAdapter ) external onlyRole(GOVERNOR_ROLE) { ITermDiscountRateAdapter newDiscountRateAdapter = ITermDiscountRateAdapter( newAdapter ); require( address(newDiscountRateAdapter.currTermController()) != address(0) ); TERM_VAULT_EVENT_EMITTER.emitDiscountRateAdapterUpdated( address(strategyState.discountRateAdapter), newAdapter ); strategyState.discountRateAdapter = newDiscountRateAdapter; } /** * @notice Set the weighted time to maturity cap * @param newTimeToMaturityThreshold The new weighted time to maturity cap */ function setTimeToMaturityThreshold( uint256 newTimeToMaturityThreshold ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitTimeToMaturityThresholdUpdated( strategyState.timeToMaturityThreshold, newTimeToMaturityThreshold ); strategyState.timeToMaturityThreshold = newTimeToMaturityThreshold; } /** * @notice Set the required reserve ratio * @dev This function can only be called by management * @param newRequiredReserveRatio The new required reserve ratio (in 1e18 precision) */ function setRequiredReserveRatio( uint256 newRequiredReserveRatio ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRequiredReserveRatioUpdated( strategyState.requiredReserveRatio, newRequiredReserveRatio ); strategyState.requiredReserveRatio = newRequiredReserveRatio; } /** * @notice Set the repoToken concentration limit * @param newRepoTokenConcentrationLimit The new repoToken concentration limit */ function setRepoTokenConcentrationLimit( uint256 newRepoTokenConcentrationLimit ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRepoTokenConcentrationLimitUpdated( strategyState.repoTokenConcentrationLimit, newRepoTokenConcentrationLimit ); strategyState .repoTokenConcentrationLimit = newRepoTokenConcentrationLimit; } /** * @notice Set the markup that the vault will receive in excess of the oracle rate * @param newDiscountRateMarkup The new auction rate markup */ function setDiscountRateMarkup( uint256 newDiscountRateMarkup ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitDiscountRateMarkupUpdated( strategyState.discountRateMarkup, newDiscountRateMarkup ); strategyState.discountRateMarkup = newDiscountRateMarkup; } /** * @notice Set the collateral token parameters * @param tokenAddr The address of the collateral token to be accepted * @param minCollateralRatio The minimum collateral ratio accepted by the strategy */ function setCollateralTokenParams( address tokenAddr, uint256 minCollateralRatio ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitMinCollateralRatioUpdated( tokenAddr, minCollateralRatio ); repoTokenListData.collateralTokenParams[tokenAddr] = minCollateralRatio; } function setRepoTokenBlacklist( address repoToken, bool blacklisted ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRepoTokenBlacklistUpdated( repoToken, blacklisted ); repoTokenBlacklist[repoToken] = blacklisted; } /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ function symbol() external view returns (string memory) { return tokenSymbol; } /** * @notice Calculates the total value of all assets managed by the strategy * @return The total asset value in the purchase token precision * * @dev This function aggregates the total liquid balance, the present value of all repoTokens, * and the present value of all pending offers to calculate the total asset value. */ function totalAssetValue() external view returns (uint256) { return _totalAssetValue(_totalLiquidBalance()); } /** * @notice Get the total liquid balance of the assets managed by the strategy * @return The total liquid balance in the purchase token precision * * @dev This function aggregates the balance of the underlying asset held directly by the strategy * and the balance of the asset held in the Yearn Vault to calculate the total liquid balance. */ function totalLiquidBalance() external view returns (uint256) { return _totalLiquidBalance(); } /** * @notice Calculate the liquid reserve ratio * @param liquidBalance The current liquid balance of the strategy * @return The liquid reserve ratio in 1e18 precision * * @dev This function calculates the ratio of liquid balance to total asset value. * It returns 0 if the total asset value is 0 to avoid division by zero. */ function _liquidReserveRatio( uint256 liquidBalance ) internal view returns (uint256) { uint256 assetValue = _totalAssetValue(liquidBalance); if (assetValue == 0) return 0; return (liquidBalance * 1e18) / assetValue; } /** * @notice Get the current liquid reserve ratio of the strategy * @return The current liquid reserve ratio in 1e18 precision * * @dev This function calculates the liquid reserve ratio based on the current * total liquid balance of the strategy. */ function liquidReserveRatio() external view returns (uint256) { return _liquidReserveRatio(_totalLiquidBalance()); } /** * @notice Returns an array of addresses representing the repoTokens currently held by the strategy * @return address[] An array of addresses of the repoTokens held by the strategy * * @dev This function calls the `holdings` function from the `RepoTokenList` library to get the list * of repoTokens currently held in the `RepoTokenListData` structure. */ function repoTokenHoldings() external view returns (address[] memory) { return repoTokenListData.holdings(); } /** * @notice Get an array of pending offers submitted into Term auctions * @return bytes32[] An array of `bytes32` values representing the IDs of the pending offers * * @dev This function calls the `pendingOffers` function from the `TermAuctionList` library to get the list * of pending offers currently submitted into Term auctions from the `TermAuctionListData` structure. */ function pendingOffers() external view returns (bytes32[] memory) { return termAuctionListData.pendingOffers(); } /** * @notice Calculate the concentration ratio of a specific repoToken in the strategy * @param repoToken The address of the repoToken to calculate the concentration for * @return The concentration ratio of the repoToken in the strategy (in 1e18 precision) * * @dev This function computes the current concentration ratio of a specific repoToken * in the strategy's portfolio. It reverts if the repoToken address is zero. The calculation * is based on the current total asset value and does not consider any additional purchases * or removals of the repoToken. */ function getRepoTokenConcentrationRatio( address repoToken ) external view returns (uint256) { if (repoToken == address(0)) { revert RepoTokenList.InvalidRepoToken(address(0)); } return _getRepoTokenConcentrationRatio( repoToken, 0, _totalAssetValue(_totalLiquidBalance()), 0 ); } /** * @notice Simulates the weighted time to maturity for a specified repoToken and amount, including the impact on the entire strategy's holdings * @param repoToken The address of the repoToken to be simulated * @param amount The amount of the repoToken to be simulated * @return simulatedWeightedMaturity The simulated weighted time to maturity for the entire strategy * @return simulatedRepoTokenConcentrationRatio The concentration ratio of the repoToken in the strategy (in 1e18 precision) * @return simulatedLiquidityRatio The simulated liquidity ratio after the transaction * * @dev This function simulates the effects of a potential transaction on the strategy's key metrics. * It calculates the new weighted time to maturity and liquidity ratio, considering the specified * repoToken and amount. For existing repoTokens, use address(0) as the repoToken parameter. * The function performs various checks and calculations, including: * - Validating the repoToken (if not address(0)) * - Calculating the present value of the transaction * - Estimating the impact on the strategy's liquid balance * - Computing the new weighted maturity and liquidity ratio */ function simulateTransaction( address repoToken, uint256 amount ) external view returns ( uint256 simulatedWeightedMaturity, uint256 simulatedRepoTokenConcentrationRatio, uint256 simulatedLiquidityRatio ) { // do not validate if we are simulating with existing repoTokens uint256 liquidBalance = _totalLiquidBalance(); uint256 repoTokenAmountInBaseAssetPrecision; uint256 proceeds; if (repoToken != address(0)) { if (!_isTermDeployed(repoToken)) { revert RepoTokenList.InvalidRepoToken(repoToken); } ( bool isRepoTokenValid, uint256 redemptionTimestamp ) = repoTokenListData.validateRepoToken( ITermRepoToken(repoToken), address(asset) ); if (!isRepoTokenValid) { revert RepoTokenList.InvalidRepoToken(repoToken); } uint256 discountRate = strategyState .discountRateAdapter .getDiscountRate(repoToken); uint256 repoRedemptionHaircut = strategyState .discountRateAdapter .repoRedemptionHaircut(repoToken); repoTokenAmountInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( repoToken, amount, PURCHASE_TOKEN_PRECISION, repoRedemptionHaircut ); proceeds = RepoTokenUtils.calculatePresentValue( repoTokenAmountInBaseAssetPrecision, PURCHASE_TOKEN_PRECISION, redemptionTimestamp, discountRate + strategyState.discountRateMarkup ); } simulatedWeightedMaturity = _calculateWeightedMaturity( repoToken, amount, liquidBalance - proceeds ); if (repoToken != address(0)) { simulatedRepoTokenConcentrationRatio = _getRepoTokenConcentrationRatio( repoToken, repoTokenAmountInBaseAssetPrecision, _totalAssetValue(liquidBalance), proceeds ); } uint256 assetValue = _totalAssetValue(liquidBalance); if (assetValue == 0) { simulatedLiquidityRatio = 0; } else { simulatedLiquidityRatio = ((liquidBalance - proceeds) * 10 ** 18) / assetValue; } } /** * @notice Calculates the present value of a specified repoToken based on its discount rate, redemption timestamp, and amount * @param repoToken The address of the repoToken * @param discountRate The discount rate to be used in the present value calculation * @param amount The amount of the repoToken to be discounted * @return uint256 The present value of the specified repoToken and amount * * @dev This function retrieves the redemption timestamp, calculates the repoToken precision, * normalizes the repoToken amount to base asset precision, and calculates the present value * using the provided discount rate and redemption timestamp. */ function calculateRepoTokenPresentValue( address repoToken, uint256 discountRate, uint256 amount ) public view returns (uint256) { (uint256 redemptionTimestamp, , , ) = ITermRepoToken(repoToken) .config(); uint256 repoTokenAmountInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( repoToken, amount, PURCHASE_TOKEN_PRECISION, strategyState.discountRateAdapter.repoRedemptionHaircut( repoToken ) ); return RepoTokenUtils.calculatePresentValue( repoTokenAmountInBaseAssetPrecision, PURCHASE_TOKEN_PRECISION, redemptionTimestamp, discountRate ); } /** * @notice Calculates the present value of a specified repoToken held by the strategy * @param repoToken The address of the repoToken to value * @return uint256 The present value of the specified repoToken * * @dev This function calculates the present value of the specified repoToken from both * the `repoTokenListData` and `termAuctionListData` structures, then sums these values * to provide a comprehensive valuation. */ function getRepoTokenHoldingValue( address repoToken ) public view returns (uint256) { uint256 repoTokenHoldingPV; if (repoTokenListData.discountRates[repoToken] != 0) { address tokenTermController; if (strategyState.currTermController.isTermDeployed(repoToken)) { tokenTermController = address(strategyState.currTermController); } else if ( strategyState.prevTermController.isTermDeployed(repoToken) ) { tokenTermController = address(strategyState.prevTermController); } repoTokenHoldingPV = calculateRepoTokenPresentValue( repoToken, strategyState.discountRateAdapter.getDiscountRate( tokenTermController, repoToken ), ITermRepoToken(repoToken).balanceOf(address(this)) ); } return repoTokenHoldingPV + termAuctionListData.getPresentValue( repoTokenListData, strategyState.discountRateAdapter, PURCHASE_TOKEN_PRECISION, repoToken ); } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @dev Withdraw assets from the Yearn vault * @param amount The amount to withdraw */ function _withdrawAsset(uint256 amount) private { YEARN_VAULT.withdraw(amount, address(this), address(this)); } /** * @dev Retrieves the asset balance from the Yearn Vault * @return The balance of assets in the purchase token precision */ function _assetBalance() private view returns (uint256) { return YEARN_VAULT.convertToAssets(YEARN_VAULT.balanceOf(address(this))); } /** * @notice Calculates the total liquid balance of the assets managed by the strategy * @return uint256 The total liquid balance of the assets * * @dev This function aggregates the balance of the underlying asset held directly by the strategy * and the balance of the asset held in the Yearn Vault to calculate the total liquid balance. */ function _totalLiquidBalance() private view returns (uint256) { uint256 underlyingBalance = IERC20(asset).balanceOf(address(this)); return _assetBalance() + underlyingBalance; } /** * @notice Calculates the total value of all assets managed by the strategy (internal function) * @return totalValue The total value of all assets * * @dev This function aggregates the total liquid balance, the present value of all repoTokens, * and the present value of all pending offers to calculate the total asset value. */ function _totalAssetValue( uint256 liquidBalance ) internal view returns (uint256 totalValue) { return liquidBalance + repoTokenListData.getPresentValue( strategyState.discountRateAdapter, PURCHASE_TOKEN_PRECISION ) + termAuctionListData.getPresentValue( repoTokenListData, strategyState.discountRateAdapter, PURCHASE_TOKEN_PRECISION, address(0) ); } /** * @notice Calculates the concentration ratio of a specific repoToken in the strategy * @param repoToken The address of the repoToken to calculate the concentration for * @param repoTokenAmountInBaseAssetPrecision The amount of the repoToken in base asset precision to be added * @param assetValue The current total asset value of the strategy * @param liquidBalanceToRemove The amount of liquid balance to be removed from the strategy * @return The concentration ratio of the repoToken in the strategy (in 1e18 precision) * * @dev This function computes the concentration ratio of a specific repoToken, considering both * existing holdings and a potential new addition. It adjusts the total asset value, normalizes * values to 1e18 precision, and handles the case where total asset value might be zero. */ function _getRepoTokenConcentrationRatio( address repoToken, uint256 repoTokenAmountInBaseAssetPrecision, uint256 assetValue, uint256 liquidBalanceToRemove ) private view returns (uint256) { // Retrieve the current value of the repoToken held by the strategy and add the new repoToken amount uint256 repoTokenValue = getRepoTokenHoldingValue(repoToken) + repoTokenAmountInBaseAssetPrecision; // Retrieve the total asset value of the strategy and adjust it for the new repoToken amount and liquid balance to be removed uint256 adjustedTotalAssetValue = assetValue + repoTokenAmountInBaseAssetPrecision - liquidBalanceToRemove; // Normalize the repoToken value and total asset value to 1e18 precision repoTokenValue = (repoTokenValue * 1e18) / PURCHASE_TOKEN_PRECISION; adjustedTotalAssetValue = (adjustedTotalAssetValue * 1e18) / PURCHASE_TOKEN_PRECISION; // Calculate the repoToken concentration return adjustedTotalAssetValue == 0 ? 0 : (repoTokenValue * 1e18) / adjustedTotalAssetValue; } /** * @notice Validate the concentration of a repoToken against the strategy's limit * @param repoToken The address of the repoToken to validate * @param repoTokenAmountInBaseAssetPrecision The amount of the repoToken in base asset precision * @param assetValue The current total asset value of the strategy * @param liquidBalanceToRemove The amount of liquid balance to be removed from the strategy * * @dev This function calculates the concentration ratio of the specified repoToken * and compares it against the predefined concentration limit. It reverts with a * RepoTokenConcentrationTooHigh error if the concentration exceeds the limit. */ function _validateRepoTokenConcentration( address repoToken, uint256 repoTokenAmountInBaseAssetPrecision, uint256 assetValue, uint256 liquidBalanceToRemove ) private view { uint256 repoTokenConcentration = _getRepoTokenConcentrationRatio( repoToken, repoTokenAmountInBaseAssetPrecision, assetValue, liquidBalanceToRemove ); // Check if the repoToken concentration exceeds the predefined limit if ( repoTokenConcentration > strategyState.repoTokenConcentrationLimit ) { revert RepoTokenConcentrationTooHigh(repoToken); } } /** * @notice Calculates the weighted time to maturity for the strategy's holdings, including the impact of a specified repoToken and amount * @param repoToken The address of the repoToken (optional) * @param repoTokenAmount The amount of the repoToken to be included in the calculation * @param liquidBalance The liquid balance of the strategy * @return uint256 The weighted time to maturity in seconds for the entire strategy, including the specified repoToken and amount * * @dev This function aggregates the cumulative weighted time to maturity and the cumulative amount of both existing repoTokens * and offers, then calculates the weighted time to maturity for the entire strategy. It considers both repoTokens and auction offers. * The `repoToken` and `repoTokenAmount` parameters are optional and provide flexibility to adjust the calculations to include * the provided repoToken amount. If `repoToken` is set to `address(0)` or `repoTokenAmount` is `0`, the function calculates * the cumulative data without specific token adjustments. */ function _calculateWeightedMaturity( address repoToken, uint256 repoTokenAmount, uint256 liquidBalance ) private view returns (uint256) { // Initialize cumulative weighted time to maturity and cumulative amount uint256 cumulativeWeightedTimeToMaturity; // in seconds uint256 cumulativeAmount; // in purchase token precision // Get cumulative data from repoToken list ( uint256 cumulativeRepoTokenWeightedTimeToMaturity, uint256 cumulativeRepoTokenAmount, bool foundInRepoTokenList ) = repoTokenListData.getCumulativeRepoTokenData( strategyState.discountRateAdapter, repoToken, repoTokenAmount, PURCHASE_TOKEN_PRECISION ); // Accumulate repoToken data cumulativeWeightedTimeToMaturity += cumulativeRepoTokenWeightedTimeToMaturity; cumulativeAmount += cumulativeRepoTokenAmount; ( uint256 cumulativeOfferWeightedTimeToMaturity, uint256 cumulativeOfferAmount, bool foundInOfferList ) = termAuctionListData.getCumulativeOfferData( repoTokenListData, strategyState.discountRateAdapter, repoToken, repoTokenAmount, PURCHASE_TOKEN_PRECISION ); // Accumulate offer data cumulativeWeightedTimeToMaturity += cumulativeOfferWeightedTimeToMaturity; cumulativeAmount += cumulativeOfferAmount; if ( !foundInRepoTokenList && !foundInOfferList && repoToken != address(0) ) { uint256 repoRedemptionHaircut = strategyState .discountRateAdapter .repoRedemptionHaircut(repoToken); uint256 repoTokenAmountInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( repoToken, repoTokenAmount, PURCHASE_TOKEN_PRECISION, repoRedemptionHaircut ); cumulativeAmount += repoTokenAmountInBaseAssetPrecision; cumulativeWeightedTimeToMaturity += RepoTokenList .getRepoTokenWeightedTimeToMaturity( repoToken, repoTokenAmountInBaseAssetPrecision ); } // Avoid division by zero if (cumulativeAmount == 0 && liquidBalance == 0) { return 0; } // Calculate and return weighted time to maturity // time * purchaseTokenPrecision / purchaseTokenPrecision return cumulativeWeightedTimeToMaturity / (cumulativeAmount + liquidBalance); } /** * @notice Checks if a term contract is marked as deployed in either the current or previous term controller * @param termContract The address of the term contract to check * @return bool True if the term contract is deployed, false otherwise * * @dev This function first checks the current term controller, then the previous one if necessary. * It handles cases where either controller might be unset (address(0)). */ function _isTermDeployed(address termContract) private view returns (bool) { ITermController currTermController = strategyState.currTermController; ITermController prevTermController = strategyState.prevTermController; if ( address(currTermController) != address(0) && currTermController.isTermDeployed(termContract) ) { return true; } if ( address(prevTermController) != address(0) && prevTermController.isTermDeployed(termContract) ) { return true; } return false; } /** * @notice Rebalances the strategy's assets by sweeping assets and redeeming matured repoTokens * @param liquidAmountRequired The amount of liquid assets required to be maintained by the strategy * * @dev This function removes completed auction offers, redeems matured repoTokens, and adjusts the underlying * balance to maintain the required liquidity. It ensures that the strategy has sufficient liquid assets while * optimizing asset allocation. */ function _redeemRepoTokens(uint256 liquidAmountRequired) private { // Remove completed auction offers termAuctionListData.removeCompleted( repoTokenListData, strategyState.discountRateAdapter, address(asset) ); // Remove and redeem matured repoTokens repoTokenListData.removeAndRedeemMaturedTokens(); uint256 liquidity = IERC20(asset).balanceOf(address(this)); // Deposit excess underlying balance into Yearn Vault if (liquidity > liquidAmountRequired) { unchecked { YEARN_VAULT.deposit( liquidity - liquidAmountRequired, address(this) ); } // Withdraw shortfall from Yearn Vault to meet required liquidity } else if (liquidity < liquidAmountRequired) { unchecked { _withdrawAsset(liquidAmountRequired - liquidity); } } } /*////////////////////////////////////////////////////////////// STRATEGIST FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Validates a term auction and repo token, and retrieves the associated offer locker * @param termAuction The term auction contract to validate * @param repoToken The repo token address to validate * @return ITermAuctionOfferLocker The offer locker associated with the validated term auction * * @dev This function performs several validation steps: verifying term auction and repo token deployment, * matching repo token to auction's term repo ID, validating repo token against strategy requirements, * and ensuring the auction is open. It reverts with specific error messages on validation failures. */ function _validateAndGetOfferLocker( ITermAuction termAuction, address repoToken ) private view returns (ITermAuctionOfferLocker) { // Verify that the term auction and repo token are valid and deployed by term if (!_isTermDeployed(address(termAuction))) { revert InvalidTermAuction(address(termAuction)); } if (!_isTermDeployed(repoToken)) { revert RepoTokenList.InvalidRepoToken(repoToken); } if ( termAuction.termRepoId() != ITermRepoToken(repoToken).termRepoId() ) { revert RepoTokenList.InvalidRepoToken(repoToken); } // Validate purchase token, min collateral ratio and insert the repoToken if necessary (bool isValid, ) = repoTokenListData.validateRepoToken( ITermRepoToken(repoToken), address(asset) ); if (!isValid) { revert RepoTokenList.InvalidRepoToken(repoToken); } // Prepare and submit the offer ITermAuctionOfferLocker offerLocker = ITermAuctionOfferLocker( termAuction.termAuctionOfferLocker() ); if ( block.timestamp <= offerLocker.auctionStartTime() || block.timestamp >= offerLocker.revealTime() ) { revert AuctionNotOpen(); } return offerLocker; } /** * @notice Submits an offer into a term auction for a specified repoToken * @param termAuction The address of the term auction * @param repoToken The address of the repoToken * @param idHash The hash of the offer ID * @param offerPriceHash The hash of the offer price * @param purchaseTokenAmount The amount of purchase tokens being offered * @return offerIds An array of offer IDs for the submitted offers * * @dev This function validates the underlying repoToken, checks concentration limits, ensures the auction is open, * and rebalances liquidity to support the offer submission. It handles both new offers and edits to existing offers. */ function submitAuctionOffer( ITermAuction termAuction, address repoToken, bytes32 idHash, bytes32 offerPriceHash, uint256 purchaseTokenAmount ) external whenNotPaused notBlacklisted(repoToken) onlyManagement returns (bytes32[] memory offerIds) { if (purchaseTokenAmount == 0) { revert ZeroPurchaseTokenAmount(); } ITermAuctionOfferLocker offerLocker = _validateAndGetOfferLocker( termAuction, repoToken ); // Sweep assets, redeem matured repoTokens and ensure liquid balances up to date _redeemRepoTokens(0); uint256 newOfferAmount = purchaseTokenAmount; uint256 currentOfferAmount = termAuctionListData .offers[idHash] .offerAmount; // Submit the offer and lock it in the auction ITermAuctionOfferLocker.TermAuctionOfferSubmission memory offer; offer.id = idHash; offer.offeror = address(this); offer.offerPriceHash = offerPriceHash; offer.amount = purchaseTokenAmount; offer.purchaseToken = address(asset); // InsufficientLiquidBalance checked inside _submitOffer offerIds = _submitOffer( termAuction, offerLocker, offer, repoToken, newOfferAmount, currentOfferAmount ); // Retrieve the total liquid balance uint256 liquidBalance = _totalLiquidBalance(); uint256 totalAssetValue = _totalAssetValue(liquidBalance); require(totalAssetValue > 0); uint256 liquidReserveRatio = (liquidBalance * 1e18) / totalAssetValue; // NOTE: we require totalAssetValue > 0 above // Check that new offer does not violate reserve ratio constraint if (liquidReserveRatio < strategyState.requiredReserveRatio) { revert BalanceBelowRequiredReserveRatio(); } // Calculate the resulting weighted time to maturity // Passing in 0 adjustment because offer and balance already updated uint256 resultingWeightedTimeToMaturity = _calculateWeightedMaturity( address(0), 0, liquidBalance ); // Check if the resulting weighted time to maturity exceeds the threshold if ( resultingWeightedTimeToMaturity > strategyState.timeToMaturityThreshold ) { revert TimeToMaturityAboveThreshold(); } // Passing in 0 amount and 0 liquid balance adjustment because offer and balance already updated _validateRepoTokenConcentration(repoToken, 0, totalAssetValue, 0); } /** * @dev Submits an offer to a term auction and locks it using the offer locker. * @param auction The term auction contract * @param offerLocker The offer locker contract * @param offer The offer details * @param repoToken The address of the repoToken * @param newOfferAmount The amount of the new offer * @param currentOfferAmount The amount of the current offer, if it exists * @return offerIds An array of offer IDs for the submitted offers */ function _submitOffer( ITermAuction auction, ITermAuctionOfferLocker offerLocker, ITermAuctionOfferLocker.TermAuctionOfferSubmission memory offer, address repoToken, uint256 newOfferAmount, uint256 currentOfferAmount ) private returns (bytes32[] memory offerIds) { // Retrieve the repo servicer contract ITermRepoServicer repoServicer = ITermRepoServicer( offerLocker.termRepoServicer() ); // Prepare the offer submission details ITermAuctionOfferLocker.TermAuctionOfferSubmission[] memory offerSubmissions = new ITermAuctionOfferLocker.TermAuctionOfferSubmission[]( 1 ); offerSubmissions[0] = offer; // Handle additional asset withdrawal if the new offer amount is greater than the current amount if (newOfferAmount > currentOfferAmount) { uint256 offerDebit; unchecked { // checked above offerDebit = newOfferAmount - currentOfferAmount; } uint256 liquidBalance = _totalLiquidBalance(); if (liquidBalance < offerDebit) { revert InsufficientLiquidBalance(liquidBalance, offerDebit); } _withdrawAsset(offerDebit); IERC20(asset).safeApprove( address(repoServicer.termRepoLocker()), offerDebit ); } // Submit the offer and get the offer IDs offerIds = offerLocker.lockOffers(offerSubmissions); if (offerIds.length == 0) { revert OfferNotFound(); } // Update the pending offers list if (currentOfferAmount == 0) { // new offer termAuctionListData.insertPending( offerIds[0], PendingOffer({ repoToken: repoToken, offerAmount: offer.amount, termAuction: auction, offerLocker: offerLocker }) ); } else { // Edit offer, overwrite existing PendingOffer storage pendingOffer = termAuctionListData.offers[ offerIds[0] ]; pendingOffer.offerAmount = offer.amount; } if (newOfferAmount < currentOfferAmount) { YEARN_VAULT.deposit( IERC20(asset).balanceOf(address(this)), address(this) ); } } /** * @dev Removes specified offers from a term auction and performs related cleanup. * @param termAuction The address of the term auction from which offers will be deleted. * @param offerIds An array of offer IDs to be deleted. */ function deleteAuctionOffers( address termAuction, bytes32[] calldata offerIds ) external onlyManagement { // Validate if the term auction is deployed by term if (!_isTermDeployed(termAuction)) { revert InvalidTermAuction(termAuction); } // Retrieve the auction and offer locker contracts ITermAuction auction = ITermAuction(termAuction); ITermAuctionOfferLocker offerLocker = ITermAuctionOfferLocker( auction.termAuctionOfferLocker() ); // Unlock the specified offers offerLocker.unlockOffers(offerIds); // Update the term auction list data and remove completed offers termAuctionListData.removeCompleted( repoTokenListData, strategyState.discountRateAdapter, address(asset) ); // Sweep any remaining assets and redeem repoTokens _redeemRepoTokens(0); } /** * @notice Required for post-processing after auction clos */ function auctionClosed() external { _redeemRepoTokens(0); } /*////////////////////////////////////////////////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Allows the sale of a specified amount of a repoToken in exchange for assets. * @param repoToken The address of the repoToken to be sold. * @param repoTokenAmount The amount of the repoToken to be sold. */ function sellRepoToken( address repoToken, uint256 repoTokenAmount ) external whenNotPaused notBlacklisted(repoToken) { // Ensure the amount of repoTokens to sell is greater than zero require(repoTokenAmount > 0); // Make sure repo token is valid and deployed by Term if (!_isTermDeployed(repoToken)) { revert RepoTokenList.InvalidRepoToken(repoToken); } // Validate and insert the repoToken into the list, retrieve auction rate and redemption timestamp (bool isRepoTokenValid, uint256 redemptionTimestamp) = repoTokenListData .validateAndInsertRepoToken( ITermRepoToken(repoToken), strategyState.discountRateAdapter, address(asset) ); if (!isRepoTokenValid) { revert RepoTokenList.InvalidRepoToken(repoToken); } // Sweep assets and redeem repoTokens, if needed _redeemRepoTokens(0); // Retrieve total asset value and liquid balance and ensure they are greater than zero uint256 liquidBalance = _totalLiquidBalance(); require(liquidBalance > 0); uint256 totalAssetValue = _totalAssetValue(liquidBalance); require(totalAssetValue > 0); uint256 discountRate = strategyState .discountRateAdapter .getDiscountRate(repoToken); // Calculate the repoToken amount in base asset precision uint256 repoTokenAmountInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( repoToken, repoTokenAmount, PURCHASE_TOKEN_PRECISION, strategyState.discountRateAdapter.repoRedemptionHaircut( repoToken ) ); // Calculate the proceeds from selling the repoToken uint256 proceeds = RepoTokenUtils.calculatePresentValue( repoTokenAmountInBaseAssetPrecision, PURCHASE_TOKEN_PRECISION, redemptionTimestamp, discountRate + strategyState.discountRateMarkup ); // Ensure the liquid balance is sufficient to cover the proceeds if (liquidBalance < proceeds) { revert InsufficientLiquidBalance(liquidBalance, proceeds); } // Calculate resulting time to maturity after the sale and ensure it doesn't exceed the threshold uint256 resultingTimeToMaturity = _calculateWeightedMaturity( repoToken, repoTokenAmount, liquidBalance - proceeds ); if (resultingTimeToMaturity > strategyState.timeToMaturityThreshold) { revert TimeToMaturityAboveThreshold(); } // Ensure the remaining liquid balance is above the liquidity threshold uint256 newLiquidReserveRatio = ((liquidBalance - proceeds) * 1e18) / totalAssetValue; // NOTE: we require totalAssetValue > 0 above if (newLiquidReserveRatio < strategyState.requiredReserveRatio) { revert BalanceBelowRequiredReserveRatio(); } // Validate resulting repoToken concentration to ensure it meets requirements _validateRepoTokenConcentration( repoToken, repoTokenAmountInBaseAssetPrecision, totalAssetValue, proceeds ); // withdraw from underlying vault _withdrawAsset(proceeds); // Transfer repoTokens from the sender to the contract IERC20(repoToken).safeTransferFrom( msg.sender, address(this), repoTokenAmount ); // Transfer the proceeds in assets to the sender IERC20(asset).safeTransfer(msg.sender, proceeds); } /** * @notice Constructor to initialize the Strategy contract * @param _name The name of the strategy */ constructor( string memory _name, string memory _symbol, StrategyParams memory _params ) BaseStrategy(_params._asset, _name) { YEARN_VAULT = IERC4626(_params._yearnVault); TERM_VAULT_EVENT_EMITTER = ITermVaultEvents(_params._eventEmitter); PURCHASE_TOKEN_PRECISION = 10 ** ERC20(asset).decimals(); IERC20(_params._asset).safeApprove( _params._yearnVault, type(uint256).max ); tokenSymbol = _symbol; strategyState = StrategyState({ assetVault: address(YEARN_VAULT), eventEmitter: address(TERM_VAULT_EVENT_EMITTER), governorAddress: _params._governorAddress, prevTermController: ITermController(address(0)), currTermController: ITermController(_params._termController), discountRateAdapter: ITermDiscountRateAdapter( _params._discountRateAdapter ), timeToMaturityThreshold: _params._timeToMaturityThreshold, requiredReserveRatio: _params._requiredReserveRatio, discountRateMarkup: _params._discountRateMarkup, repoTokenConcentrationLimit: _params._repoTokenConcentrationLimit }); _grantRole(GOVERNOR_ROLE, _params._governorAddress); } /*////////////////////////////////////////////////////////////// NEEDED TO BE OVERRIDDEN BY STRATEGIST //////////////////////////////////////////////////////////////*/ /** * @dev Can deploy up to '_amount' of 'asset' in the yield source. * * This function is called at the end of a {deposit} or {mint} * call. Meaning that unless a whitelist is implemented it will * be entirely permissionless and thus can be sandwiched or otherwise * manipulated. * * @param _amount The amount of 'asset' that the strategy can attempt * to deposit in the yield source. */ function _deployFunds(uint256 _amount) internal override whenNotPaused { if (depositLock) { revert DepositPaused(); } _redeemRepoTokens(0); } /** * @dev Should attempt to free the '_amount' of 'asset'. * * NOTE: The amount of 'asset' that is already loose has already * been accounted for. * * This function is called during {withdraw} and {redeem} calls. * Meaning that unless a whitelist is implemented it will be * entirely permissionless and thus can be sandwiched or otherwise * manipulated. * * Should not rely on asset.balanceOf(address(this)) calls other than * for diff accounting purposes. * * Any difference between `_amount` and what is actually freed will be * counted as a loss and passed on to the withdrawer. This means * care should be taken in times of illiquidity. It may be better to revert * if withdraws are simply illiquid so not to realize incorrect losses. * * @param _amount, The amount of 'asset' to be freed. */ function _freeFunds(uint256 _amount) internal override whenNotPaused { _redeemRepoTokens(_amount); } /** * @dev Internal function to harvest all rewards, redeploy any idle * funds and return an accurate accounting of all funds currently * held by the Strategy. * * This should do any needed harvesting, rewards selling, accrual, * redepositing etc. to get the most accurate view of current assets. * * NOTE: All applicable assets including loose assets should be * accounted for in this function. * * Care should be taken when relying on oracles or swap values rather * than actual amounts as all Strategy profit/loss accounting will * be done based on this returned value. * * This can still be called post a shutdown, a strategist can check * `TokenizedStrategy.isShutdown()` to decide if funds should be * redeployed or simply realize any profits/losses. * * @return _totalAssets A trusted and accurate account for the total * amount of 'asset' the strategy currently holds including idle funds. */ function _harvestAndReport() internal override whenNotPaused returns (uint256 _totalAssets) { _redeemRepoTokens(0); return _totalAssetValue(_totalLiquidBalance()); } /*////////////////////////////////////////////////////////////// OPTIONAL TO OVERRIDE BY STRATEGIST //////////////////////////////////////////////////////////////*/ /** * @notice Gets the max amount of `asset` that can be withdrawn. * @dev Defaults to an unlimited amount for any address. But can * be overridden by strategists. * * This function will be called before any withdraw or redeem to enforce * any limits desired by the strategist. This can be used for illiquid * or sandwichable strategies. * * EX: * return asset.balanceOf(yieldSource); * * This does not need to take into account the `_owner`'s share balance * or conversion rates from shares to assets. * * @param . The address that is withdrawing from the strategy. * @return . The available amount that can be withdrawn in terms of `asset` */ function availableWithdrawLimit( address /*_owner*/ ) public view override returns (uint256) { return _totalLiquidBalance(); } /** * @dev Optional function for strategist to override that can * be called in between reports. * * If '_tend' is used tendTrigger() will also need to be overridden. * * This call can only be called by a permissioned role so may be * through protected relays. * * This can be used to harvest and compound rewards, deposit idle funds, * perform needed position maintenance or anything else that doesn't need * a full report for. * * EX: A strategy that can not deposit funds without getting * sandwiched can use the tend when a certain threshold * of idle to totalAssets has been reached. * * This will have no effect on PPS of the strategy till report() is called. * * @param _totalIdle The current amount of idle funds that are available to deploy. * function _tend(uint256 _totalIdle) internal override {} */ /** * @dev Optional trigger to override if tend() will be used by the strategy. * This must be implemented if the strategy hopes to invoke _tend(). * * @return . Should return true if tend() should be called by keeper or false if not. * function _tendTrigger() internal view override returns (bool) {} */ /** * @dev Optional function for a strategist to override that will * allow management to manually withdraw deployed funds from the * yield source if a strategy is shutdown. * * This should attempt to free `_amount`, noting that `_amount` may * be more than is currently deployed. * * NOTE: This will not realize any profits or losses. A separate * {report} will be needed in order to record any profit/loss. If * a report may need to be called after a shutdown it is important * to check if the strategy is shutdown during {_harvestAndReport} * so that it does not simply re-deploy all funds that had been freed. * * EX: * if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) { * depositFunds... * } * * @param _amount The amount of asset to attempt to free. * function _emergencyWithdraw(uint256 _amount) internal override { EX: _amount = min(_amount, aToken.balanceOf(address(this))); _freeFunds(_amount); } */ }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20.sol"; import "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * _Available since v4.7._ */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// 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.4) (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. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ 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]. * * CAUTION: See Security Considerations above. */ 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 // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.18; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; // TokenizedStrategy interface used for internal view delegateCalls. import {ITokenizedStrategy} from "./interfaces/ITokenizedStrategy.sol"; /** * @title YearnV3 Base Strategy * @author yearn.finance * @notice * BaseStrategy implements all of the required functionality to * seamlessly integrate with the `TokenizedStrategy` implementation contract * allowing anyone to easily build a fully permissionless ERC-4626 compliant * Vault by inheriting this contract and overriding three simple functions. * It utilizes an immutable proxy pattern that allows the BaseStrategy * to remain simple and small. All standard logic is held within the * `TokenizedStrategy` and is reused over any n strategies all using the * `fallback` function to delegatecall the implementation so that strategists * can only be concerned with writing their strategy specific code. * * This contract should be inherited and the three main abstract methods * `_deployFunds`, `_freeFunds` and `_harvestAndReport` implemented to adapt * the Strategy to the particular needs it has to generate yield. There are * other optional methods that can be implemented to further customize * the strategy if desired. * * All default storage for the strategy is controlled and updated by the * `TokenizedStrategy`. The implementation holds a storage struct that * contains all needed global variables in a manual storage slot. This * means strategists can feel free to implement their own custom storage * variables as they need with no concern of collisions. All global variables * can be viewed within the Strategy by a simple call using the * `TokenizedStrategy` variable. IE: TokenizedStrategy.globalVariable();. */ abstract contract BaseStrategy { /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ /** * @dev Used on TokenizedStrategy callback functions to make sure it is post * a delegateCall from this address to the TokenizedStrategy. */ modifier onlySelf() { _onlySelf(); _; } /** * @dev Use to assure that the call is coming from the strategies management. */ modifier onlyManagement() { TokenizedStrategy.requireManagement(msg.sender); _; } /** * @dev Use to assure that the call is coming from either the strategies * management or the keeper. */ modifier onlyKeepers() { TokenizedStrategy.requireKeeperOrManagement(msg.sender); _; } /** * @dev Use to assure that the call is coming from either the strategies * management or the emergency admin. */ modifier onlyEmergencyAuthorized() { TokenizedStrategy.requireEmergencyAuthorized(msg.sender); _; } /** * @dev Require that the msg.sender is this address. */ function _onlySelf() internal view { require(msg.sender == address(this), "!self"); } /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /** * @dev This is the address of the TokenizedStrategy implementation * contract that will be used by all strategies to handle the * accounting, logic, storage etc. * * Any external calls to the that don't hit one of the functions * defined in this base or the strategy will end up being forwarded * through the fallback function, which will delegateCall this address. * * This address should be the same for every strategy, never be adjusted * and always be checked before any integration with the Strategy. */ address public constant tokenizedStrategyAddress = 0xBB51273D6c746910C7C06fe718f30c936170feD0; /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ /** * @dev Underlying asset the Strategy is earning yield on. * Stored here for cheap retrievals within the strategy. */ ERC20 internal immutable asset; /** * @dev This variable is set to address(this) during initialization of each strategy. * * This can be used to retrieve storage data within the strategy * contract as if it were a linked library. * * i.e. uint256 totalAssets = TokenizedStrategy.totalAssets() * * Using address(this) will mean any calls using this variable will lead * to a call to itself. Which will hit the fallback function and * delegateCall that to the actual TokenizedStrategy. */ ITokenizedStrategy internal immutable TokenizedStrategy; /** * @notice Used to initialize the strategy on deployment. * * This will set the `TokenizedStrategy` variable for easy * internal view calls to the implementation. As well as * initializing the default storage variables based on the * parameters and using the deployer for the permissioned roles. * * @param _asset Address of the underlying asset. * @param _name Name the strategy will use. */ constructor(address _asset, string memory _name) { asset = ERC20(_asset); // Set instance of the implementation for internal use. TokenizedStrategy = ITokenizedStrategy(address(this)); // Initialize the strategy's storage variables. _delegateCall( abi.encodeCall( ITokenizedStrategy.initialize, (_asset, _name, msg.sender, msg.sender, msg.sender) ) ); // Store the tokenizedStrategyAddress at the standard implementation // address storage slot so etherscan picks up the interface. This gets // stored on initialization and never updated. assembly { sstore( // keccak256('eip1967.proxy.implementation' - 1) 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, tokenizedStrategyAddress ) } } /*////////////////////////////////////////////////////////////// NEEDED TO BE OVERRIDDEN BY STRATEGIST //////////////////////////////////////////////////////////////*/ /** * @dev Can deploy up to '_amount' of 'asset' in the yield source. * * This function is called at the end of a {deposit} or {mint} * call. Meaning that unless a whitelist is implemented it will * be entirely permissionless and thus can be sandwiched or otherwise * manipulated. * * @param _amount The amount of 'asset' that the strategy can attempt * to deposit in the yield source. */ function _deployFunds(uint256 _amount) internal virtual; /** * @dev Should attempt to free the '_amount' of 'asset'. * * NOTE: The amount of 'asset' that is already loose has already * been accounted for. * * This function is called during {withdraw} and {redeem} calls. * Meaning that unless a whitelist is implemented it will be * entirely permissionless and thus can be sandwiched or otherwise * manipulated. * * Should not rely on asset.balanceOf(address(this)) calls other than * for diff accounting purposes. * * Any difference between `_amount` and what is actually freed will be * counted as a loss and passed on to the withdrawer. This means * care should be taken in times of illiquidity. It may be better to revert * if withdraws are simply illiquid so not to realize incorrect losses. * * @param _amount, The amount of 'asset' to be freed. */ function _freeFunds(uint256 _amount) internal virtual; /** * @dev Internal function to harvest all rewards, redeploy any idle * funds and return an accurate accounting of all funds currently * held by the Strategy. * * This should do any needed harvesting, rewards selling, accrual, * redepositing etc. to get the most accurate view of current assets. * * NOTE: All applicable assets including loose assets should be * accounted for in this function. * * Care should be taken when relying on oracles or swap values rather * than actual amounts as all Strategy profit/loss accounting will * be done based on this returned value. * * This can still be called post a shutdown, a strategist can check * `TokenizedStrategy.isShutdown()` to decide if funds should be * redeployed or simply realize any profits/losses. * * @return _totalAssets A trusted and accurate account for the total * amount of 'asset' the strategy currently holds including idle funds. */ function _harvestAndReport() internal virtual returns (uint256 _totalAssets); /*////////////////////////////////////////////////////////////// OPTIONAL TO OVERRIDE BY STRATEGIST //////////////////////////////////////////////////////////////*/ /** * @dev Optional function for strategist to override that can * be called in between reports. * * If '_tend' is used tendTrigger() will also need to be overridden. * * This call can only be called by a permissioned role so may be * through protected relays. * * This can be used to harvest and compound rewards, deposit idle funds, * perform needed position maintenance or anything else that doesn't need * a full report for. * * EX: A strategy that can not deposit funds without getting * sandwiched can use the tend when a certain threshold * of idle to totalAssets has been reached. * * This will have no effect on PPS of the strategy till report() is called. * * @param _totalIdle The current amount of idle funds that are available to deploy. */ function _tend(uint256 _totalIdle) internal virtual {} /** * @dev Optional trigger to override if tend() will be used by the strategy. * This must be implemented if the strategy hopes to invoke _tend(). * * @return . Should return true if tend() should be called by keeper or false if not. */ function _tendTrigger() internal view virtual returns (bool) { return false; } /** * @notice Returns if tend() should be called by a keeper. * * @return . Should return true if tend() should be called by keeper or false if not. * @return . Calldata for the tend call. */ function tendTrigger() external view virtual returns (bool, bytes memory) { return ( // Return the status of the tend trigger. _tendTrigger(), // And the needed calldata either way. abi.encodeWithSelector(ITokenizedStrategy.tend.selector) ); } /** * @notice Gets the max amount of `asset` that an address can deposit. * @dev Defaults to an unlimited amount for any address. But can * be overridden by strategists. * * This function will be called before any deposit or mints to enforce * any limits desired by the strategist. This can be used for either a * traditional deposit limit or for implementing a whitelist etc. * * EX: * if(isAllowed[_owner]) return super.availableDepositLimit(_owner); * * This does not need to take into account any conversion rates * from shares to assets. But should know that any non max uint256 * amounts may be converted to shares. So it is recommended to keep * custom amounts low enough as not to cause overflow when multiplied * by `totalSupply`. * * @param . The address that is depositing into the strategy. * @return . The available amount the `_owner` can deposit in terms of `asset` */ function availableDepositLimit( address /*_owner*/ ) public view virtual returns (uint256) { return type(uint256).max; } /** * @notice Gets the max amount of `asset` that can be withdrawn. * @dev Defaults to an unlimited amount for any address. But can * be overridden by strategists. * * This function will be called before any withdraw or redeem to enforce * any limits desired by the strategist. This can be used for illiquid * or sandwichable strategies. It should never be lower than `totalIdle`. * * EX: * return TokenIzedStrategy.totalIdle(); * * This does not need to take into account the `_owner`'s share balance * or conversion rates from shares to assets. * * @param . The address that is withdrawing from the strategy. * @return . The available amount that can be withdrawn in terms of `asset` */ function availableWithdrawLimit( address /*_owner*/ ) public view virtual returns (uint256) { return type(uint256).max; } /** * @dev Optional function for a strategist to override that will * allow management to manually withdraw deployed funds from the * yield source if a strategy is shutdown. * * This should attempt to free `_amount`, noting that `_amount` may * be more than is currently deployed. * * NOTE: This will not realize any profits or losses. A separate * {report} will be needed in order to record any profit/loss. If * a report may need to be called after a shutdown it is important * to check if the strategy is shutdown during {_harvestAndReport} * so that it does not simply re-deploy all funds that had been freed. * * EX: * if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) { * depositFunds... * } * * @param _amount The amount of asset to attempt to free. */ function _emergencyWithdraw(uint256 _amount) internal virtual {} /*////////////////////////////////////////////////////////////// TokenizedStrategy HOOKS //////////////////////////////////////////////////////////////*/ /** * @notice Can deploy up to '_amount' of 'asset' in yield source. * @dev Callback for the TokenizedStrategy to call during a {deposit} * or {mint} to tell the strategy it can deploy funds. * * Since this can only be called after a {deposit} or {mint} * delegateCall to the TokenizedStrategy msg.sender == address(this). * * Unless a whitelist is implemented this will be entirely permissionless * and thus can be sandwiched or otherwise manipulated. * * @param _amount The amount of 'asset' that the strategy can * attempt to deposit in the yield source. */ function deployFunds(uint256 _amount) external virtual onlySelf { _deployFunds(_amount); } /** * @notice Should attempt to free the '_amount' of 'asset'. * @dev Callback for the TokenizedStrategy to call during a withdraw * or redeem to free the needed funds to service the withdraw. * * This can only be called after a 'withdraw' or 'redeem' delegateCall * to the TokenizedStrategy so msg.sender == address(this). * * @param _amount The amount of 'asset' that the strategy should attempt to free up. */ function freeFunds(uint256 _amount) external virtual onlySelf { _freeFunds(_amount); } /** * @notice Returns the accurate amount of all funds currently * held by the Strategy. * @dev Callback for the TokenizedStrategy to call during a report to * get an accurate accounting of assets the strategy controls. * * This can only be called after a report() delegateCall to the * TokenizedStrategy so msg.sender == address(this). * * @return . A trusted and accurate account for the total amount * of 'asset' the strategy currently holds including idle funds. */ function harvestAndReport() external virtual onlySelf returns (uint256) { return _harvestAndReport(); } /** * @notice Will call the internal '_tend' when a keeper tends the strategy. * @dev Callback for the TokenizedStrategy to initiate a _tend call in the strategy. * * This can only be called after a tend() delegateCall to the TokenizedStrategy * so msg.sender == address(this). * * We name the function `tendThis` so that `tend` calls are forwarded to * the TokenizedStrategy. * @param _totalIdle The amount of current idle funds that can be * deployed during the tend */ function tendThis(uint256 _totalIdle) external virtual onlySelf { _tend(_totalIdle); } /** * @notice Will call the internal '_emergencyWithdraw' function. * @dev Callback for the TokenizedStrategy during an emergency withdraw. * * This can only be called after a emergencyWithdraw() delegateCall to * the TokenizedStrategy so msg.sender == address(this). * * We name the function `shutdownWithdraw` so that `emergencyWithdraw` * calls are forwarded to the TokenizedStrategy. * * @param _amount The amount of asset to attempt to free. */ function shutdownWithdraw(uint256 _amount) external virtual onlySelf { _emergencyWithdraw(_amount); } /** * @dev Function used to delegate call the TokenizedStrategy with * certain `_calldata` and return any return values. * * This is used to setup the initial storage of the strategy, and * can be used by strategist to forward any other call to the * TokenizedStrategy implementation. * * @param _calldata The abi encoded calldata to use in delegatecall. * @return . The return value if the call was successful in bytes. */ function _delegateCall( bytes memory _calldata ) internal returns (bytes memory) { // Delegate call the tokenized strategy with provided calldata. (bool success, bytes memory result) = tokenizedStrategyAddress .delegatecall(_calldata); // If the call reverted. Return the error. if (!success) { assembly { let ptr := mload(0x40) let size := returndatasize() returndatacopy(ptr, 0, size) revert(ptr, size) } } // Return the result. return result; } /** * @dev Execute a function on the TokenizedStrategy and return any value. * * This fallback function will be executed when any of the standard functions * defined in the TokenizedStrategy are called since they wont be defined in * this contract. * * It will delegatecall the TokenizedStrategy implementation with the exact * calldata and return any relevant values. * */ fallback() external { // load our target address address _tokenizedStrategyAddress = tokenizedStrategyAddress; // Execute external function using delegatecall and return any value. assembly { // Copy function selector and any arguments. calldatacopy(0, 0, calldatasize()) // Execute function delegatecall. let result := delegatecall( gas(), _tokenizedStrategyAddress, 0, calldatasize(), 0, 0 ) // Get any return value returndatacopy(0, 0, returndatasize()) // Return any return value or error back to the caller switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.18; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; // Interface that implements the 4626 standard and the implementation functions interface ITokenizedStrategy is IERC4626, IERC20Permit { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event StrategyShutdown(); event NewTokenizedStrategy( address indexed strategy, address indexed asset, string apiVersion ); event Reported( uint256 profit, uint256 loss, uint256 protocolFees, uint256 performanceFees ); event UpdatePerformanceFeeRecipient( address indexed newPerformanceFeeRecipient ); event UpdateKeeper(address indexed newKeeper); event UpdatePerformanceFee(uint16 newPerformanceFee); event UpdateManagement(address indexed newManagement); event UpdateEmergencyAdmin(address indexed newEmergencyAdmin); event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime); event UpdatePendingManagement(address indexed newPendingManagement); /*////////////////////////////////////////////////////////////// INITIALIZATION //////////////////////////////////////////////////////////////*/ function initialize( address _asset, string memory _name, address _management, address _performanceFeeRecipient, address _keeper ) external; /*////////////////////////////////////////////////////////////// NON-STANDARD 4626 OPTIONS //////////////////////////////////////////////////////////////*/ function withdraw( uint256 assets, address receiver, address owner, uint256 maxLoss ) external returns (uint256); function redeem( uint256 shares, address receiver, address owner, uint256 maxLoss ) external returns (uint256); /*////////////////////////////////////////////////////////////// MODIFIER HELPERS //////////////////////////////////////////////////////////////*/ function requireManagement(address _sender) external view; function requireKeeperOrManagement(address _sender) external view; function requireEmergencyAuthorized(address _sender) external view; /*////////////////////////////////////////////////////////////// KEEPERS FUNCTIONS //////////////////////////////////////////////////////////////*/ function tend() external; function report() external returns (uint256 _profit, uint256 _loss); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ function MAX_FEE() external view returns (uint16); function FACTORY() external view returns (address); /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ function apiVersion() external view returns (string memory); function pricePerShare() external view returns (uint256); function management() external view returns (address); function pendingManagement() external view returns (address); function keeper() external view returns (address); function emergencyAdmin() external view returns (address); function performanceFee() external view returns (uint16); function performanceFeeRecipient() external view returns (address); function fullProfitUnlockDate() external view returns (uint256); function profitUnlockingRate() external view returns (uint256); function profitMaxUnlockTime() external view returns (uint256); function lastReport() external view returns (uint256); function isShutdown() external view returns (bool); function unlockedShares() external view returns (uint256); /*////////////////////////////////////////////////////////////// SETTERS //////////////////////////////////////////////////////////////*/ function setPendingManagement(address) external; function acceptManagement() external; function setKeeper(address _keeper) external; function setEmergencyAdmin(address _emergencyAdmin) external; function setPerformanceFee(uint16 _performanceFee) external; function setPerformanceFeeRecipient( address _performanceFeeRecipient ) external; function setProfitMaxUnlockTime(uint256 _profitMaxUnlockTime) external; function shutdownStrategy() external; function emergencyWithdraw(uint256 _amount) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; interface ITermAuction { function termAuctionOfferLocker() external view returns (address); function termRepoId() external view returns (bytes32); function auctionEndTime() external view returns (uint256); function auctionCompleted() external view returns (bool); function auctionCancelledForWithdrawal() external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; interface ITermAuctionOfferLocker { /// @dev TermAuctionOfferSubmission represents an offer submission to offeror an amount of money for a specific interest rate struct TermAuctionOfferSubmission { /// @dev For an existing offer this is the unique onchain identifier for this offer. For a new offer this is a randomized input that will be used to generate the unique onchain identifier. bytes32 id; /// @dev The address of the offeror address offeror; /// @dev Hash of the offered price as a percentage of the initial loaned amount vs amount returned at maturity. This stores 9 decimal places bytes32 offerPriceHash; /// @dev The maximum amount of purchase tokens that can be lent uint256 amount; /// @dev The address of the ERC20 purchase token address purchaseToken; } /// @dev TermAuctionOffer represents an offer to offeror an amount of money for a specific interest rate struct TermAuctionOffer { /// @dev Unique identifier for this bid bytes32 id; /// @dev The address of the offeror address offeror; /// @dev Hash of the offered price as a percentage of the initial loaned amount vs amount returned at maturity. This stores 9 decimal places bytes32 offerPriceHash; /// @dev Revealed offer price. This is not valid unless isRevealed is true. This stores 18 decimal places uint256 offerPriceRevealed; /// @dev The maximum amount of purchase tokens that can be lent uint256 amount; /// @dev The address of the ERC20 purchase token address purchaseToken; /// @dev Is offer price revealed bool isRevealed; } /// @dev TermAuctionRevealedOffer represents a revealed offer to offeror an amount of money for a specific interest rate struct TermAuctionRevealedOffer { /// @dev Unique identifier for this bid bytes32 id; /// @dev The address of the offeror address offeror; /// @dev The offered price as a percentage of the initial loaned amount vs amount returned at maturity. This stores 9 decimal places uint256 offerPriceRevealed; /// @dev The maximum amount of purchase tokens offered uint256 amount; /// @dev The address of the lent ERC20 token address purchaseToken; } function termRepoId() external view returns (bytes32); function termAuctionId() external view returns (bytes32); function auctionStartTime() external view returns (uint256); function auctionEndTime() external view returns (uint256); function revealTime() external view returns (uint256); function purchaseToken() external view returns (address); function termRepoServicer() external view returns (address); function lockedOffer( bytes32 id ) external view returns (TermAuctionOffer memory); /// @param offerSubmissions An array of offer submissions /// @return A bytes32 array of unique on chain offer ids. function lockOffers( TermAuctionOfferSubmission[] calldata offerSubmissions ) external returns (bytes32[] memory); function unlockOffers(bytes32[] calldata offerIds) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; struct AuctionMetadata { bytes32 termAuctionId; uint256 auctionClearingRate; uint256 auctionClearingBlockTimestamp; } interface ITermController { function isTermDeployed( address contractAddress ) external view returns (bool); function getProtocolReserveAddress() external view returns (address); function getTermAuctionResults( bytes32 termRepoId ) external view returns (AuctionMetadata[] memory auctionMetadata, uint8 numOfAuctions); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {ITermController} from "./ITermController.sol"; interface ITermDiscountRateAdapter { function currTermController() external view returns (ITermController); function repoRedemptionHaircut(address) external view returns (uint256); function getDiscountRate(address repoToken) external view returns (uint256); function getDiscountRate( address termController, address repoToken ) external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; interface ITermRepoCollateralManager { function maintenanceCollateralRatios( address ) external view returns (uint256); function numOfAcceptedCollateralTokens() external view returns (uint8); function collateralTokens(uint256 index) external view returns (address); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; interface ITermRepoServicer { function redeemTermRepoTokens( address redeemer, uint256 amountToRedeem ) external; function termRepoToken() external view returns (address); function termRepoLocker() external view returns (address); function purchaseToken() external view returns (address); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ITermRepoToken is IERC20 { function redemptionValue() external view returns (uint256); function config() external view returns ( uint256 redemptionTimestamp, address purchaseToken, address termRepoServicer, address termRepoCollateralManager ); function termRepoId() external view returns (bytes32); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; interface ITermVaultEvents { event VaultContractPaired(address vault); event TermControllerUpdated(address oldController, address newController); event TimeToMaturityThresholdUpdated( uint256 oldThreshold, uint256 newThreshold ); event RequiredReserveRatioUpdated( uint256 oldThreshold, uint256 newThreshold ); event DiscountRateMarkupUpdated(uint256 oldMarkup, uint256 newMarkup); event MinCollateralRatioUpdated( address collateral, uint256 minCollateralRatio ); event RepoTokenConcentrationLimitUpdated( uint256 oldLimit, uint256 newLimit ); event DepositPaused(); event DepositUnpaused(); /* event StrategyPaused(); event StrategyUnpaused(); */ event DiscountRateAdapterUpdated( address indexed oldAdapter, address indexed newAdapter ); event RepoTokenBlacklistUpdated( address indexed repoToken, bool blacklisted ); event NewGovernor(address newGovernor); function emitTermControllerUpdated( address oldController, address newController ) external; function emitTimeToMaturityThresholdUpdated( uint256 oldThreshold, uint256 newThreshold ) external; function emitRequiredReserveRatioUpdated( uint256 oldThreshold, uint256 newThreshold ) external; function emitDiscountRateMarkupUpdated( uint256 oldMarkup, uint256 newMarkup ) external; function emitMinCollateralRatioUpdated( address collateral, uint256 minCollateralRatio ) external; function emitRepoTokenConcentrationLimitUpdated( uint256 oldLimit, uint256 newLimit ) external; function emitDepositPaused() external; function emitDepositUnpaused() external; /* function emitStrategyPaused() external; function emitStrategyUnpaused() external;*/ function emitDiscountRateAdapterUpdated( address oldAdapter, address newAdapter ) external; function emitRepoTokenBlacklistUpdated( address repoToken, bool blacklisted ) external; function emitNewGovernor(address newGovernor) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {ITermController} from "./interfaces/term/ITermController.sol"; import {ITermRepoToken} from "./interfaces/term/ITermRepoToken.sol"; import {ITermRepoServicer} from "./interfaces/term/ITermRepoServicer.sol"; import {ITermRepoCollateralManager} from "./interfaces/term/ITermRepoCollateralManager.sol"; import {ITermDiscountRateAdapter} from "./interfaces/term/ITermDiscountRateAdapter.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {RepoTokenUtils} from "./RepoTokenUtils.sol"; struct RepoTokenListNode { address next; } struct RepoTokenListData { address head; mapping(address => RepoTokenListNode) nodes; mapping(address => uint256) discountRates; /// @notice keyed by collateral token mapping(address => uint256) collateralTokenParams; } /*////////////////////////////////////////////////////////////// LIBRARY: RepoTokenList //////////////////////////////////////////////////////////////*/ library RepoTokenList { address internal constant NULL_NODE = address(0); uint256 internal constant INVALID_AUCTION_RATE = 0; uint256 internal constant ZERO_AUCTION_RATE = 1; //Set to lowest nonzero number so that it is not confused with INVALID_AUCTION_RATe but still calculates as if 0. error InvalidRepoToken(address token); /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Retrieves the redemption (maturity) timestamp of a repoToken * @param repoToken The address of the repoToken * @return redemptionTimestamp The timestamp indicating when the repoToken matures * * @dev This function calls the `config()` method on the repoToken to retrieve its configuration details, * including the redemption timestamp, which it then returns. */ function getRepoTokenMaturity( address repoToken ) internal view returns (uint256 redemptionTimestamp) { (redemptionTimestamp, , , ) = ITermRepoToken(repoToken).config(); } /** * @notice Get the next node in the list * @param listData The list data * @param current The current node * @return The next node */ function _getNext( RepoTokenListData storage listData, address current ) private view returns (address) { return listData.nodes[current].next; } /** * @notice Count the number of nodes in the list * @param listData The list data * @return count The number of nodes in the list */ function _count( RepoTokenListData storage listData ) private view returns (uint256 count) { if (listData.head == NULL_NODE) return 0; address current = listData.head; while (current != NULL_NODE) { count++; current = _getNext(listData, current); } } /** * @notice Returns an array of addresses representing the repoTokens currently held in the list data * @param listData The list data * @return holdingsArray An array of addresses of the repoTokens held in the list * * @dev This function iterates through the list of repoTokens and returns their addresses in an array. * It first counts the number of repoTokens, initializes an array of that size, and then populates the array * with the addresses of the repoTokens. */ function holdings( RepoTokenListData storage listData ) internal view returns (address[] memory holdingsArray) { uint256 count = _count(listData); if (count > 0) { holdingsArray = new address[](count); uint256 i; address current = listData.head; while (current != NULL_NODE) { holdingsArray[i++] = current; current = _getNext(listData, current); } } } /** * @notice Get the weighted time to maturity of the strategy's holdings of a specified repoToken * @param repoToken The address of the repoToken * @param repoTokenBalanceInBaseAssetPrecision The balance of the repoToken in base asset precision * @return weightedTimeToMaturity The weighted time to maturity in seconds x repoToken balance in base asset precision */ function getRepoTokenWeightedTimeToMaturity( address repoToken, uint256 repoTokenBalanceInBaseAssetPrecision ) internal view returns (uint256 weightedTimeToMaturity) { uint256 currentMaturity = getRepoTokenMaturity(repoToken); if (currentMaturity > block.timestamp) { uint256 timeToMaturity = _getRepoTokenTimeToMaturity( currentMaturity ); // Not matured yet weightedTimeToMaturity = timeToMaturity * repoTokenBalanceInBaseAssetPrecision; } } /** * @notice This function calculates the cumulative weighted time to maturity and cumulative amount of all repoTokens in the list. * @param listData The list data * @param discountRateAdapter The discount rate adapter * @param repoToken The address of the repoToken (optional) * @param repoTokenAmount The amount of the repoToken (optional) * @param purchaseTokenPrecision The precision of the purchase token * @return cumulativeWeightedTimeToMaturity The cumulative weighted time to maturity for all repoTokens * @return cumulativeRepoTokenAmount The cumulative repoToken amount across all repoTokens * @return found Whether the specified repoToken was found in the list * * @dev The `repoToken` and `repoTokenAmount` parameters are optional and provide flexibility * to adjust the calculations to include the provided repoToken and amount. If `repoToken` is * set to `address(0)` or `repoTokenAmount` is `0`, the function calculates the cumulative * data without specific token adjustments. */ function getCumulativeRepoTokenData( RepoTokenListData storage listData, ITermDiscountRateAdapter discountRateAdapter, address repoToken, uint256 repoTokenAmount, uint256 purchaseTokenPrecision ) internal view returns ( uint256 cumulativeWeightedTimeToMaturity, uint256 cumulativeRepoTokenAmount, bool found ) { // Return early if the list is empty if (listData.head == NULL_NODE) return (0, 0, false); // Initialize the current pointer to the head of the list address current = listData.head; while (current != NULL_NODE) { uint256 repoTokenBalance = ITermRepoToken(current).balanceOf( address(this) ); // Process if the repo token has a positive balance if (repoTokenBalance > 0) { // Add repoTokenAmount if the current token matches the specified repoToken if (repoToken == current) { repoTokenBalance += repoTokenAmount; found = true; } // Convert the repo token balance to base asset precision uint256 repoTokenBalanceInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( current, repoTokenBalance, purchaseTokenPrecision, discountRateAdapter.repoRedemptionHaircut(current) ); // Calculate the weighted time to maturity uint256 weightedTimeToMaturity = getRepoTokenWeightedTimeToMaturity( current, repoTokenBalanceInBaseAssetPrecision ); // Accumulate the results cumulativeWeightedTimeToMaturity += weightedTimeToMaturity; cumulativeRepoTokenAmount += repoTokenBalanceInBaseAssetPrecision; } // Move to the next repo token in the list current = _getNext(listData, current); } } /** * @notice Get the present value of repoTokens * @param listData The list data * @param discountRateAdapter The discount rate adapter * @param purchaseTokenPrecision The precision of the purchase token * @return totalPresentValue The total present value of the repoTokens * @dev Aggregates the present value of all repoTokens in the list. */ function getPresentValue( RepoTokenListData storage listData, ITermDiscountRateAdapter discountRateAdapter, uint256 purchaseTokenPrecision ) internal view returns (uint256 totalPresentValue) { // If the list is empty, return 0 if (listData.head == NULL_NODE) return 0; address current = listData.head; while (current != NULL_NODE) { uint256 currentMaturity = getRepoTokenMaturity(current); uint256 repoTokenBalance = ITermRepoToken(current).balanceOf( address(this) ); uint256 discountRate = discountRateAdapter.getDiscountRate(current); // Convert repo token balance to base asset precision // (ratePrecision * repoPrecision * purchasePrecision) / (repoPrecision * ratePrecision) = purchasePrecision uint256 repoTokenBalanceInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( current, repoTokenBalance, purchaseTokenPrecision, discountRateAdapter.repoRedemptionHaircut(current) ); // Calculate present value based on maturity if (currentMaturity > block.timestamp) { totalPresentValue += RepoTokenUtils.calculatePresentValue( repoTokenBalanceInBaseAssetPrecision, purchaseTokenPrecision, currentMaturity, discountRate ); } else { totalPresentValue += repoTokenBalanceInBaseAssetPrecision; } // Move to the next token in the list current = _getNext(listData, current); } } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Calculates the time remaining until a repoToken matures * @param redemptionTimestamp The redemption timestamp of the repoToken * @return uint256 The time remaining (in seconds) until the repoToken matures * * @dev This function calculates the difference between the redemption timestamp and the current block timestamp * to determine how many seconds are left until the repoToken reaches its maturity. */ function _getRepoTokenTimeToMaturity( uint256 redemptionTimestamp ) private view returns (uint256) { return redemptionTimestamp - block.timestamp; } /** * @notice Removes and redeems matured repoTokens from the list data * @param listData The list data * * @dev Iterates through the list of repoTokens and removes those that have matured. If a matured repoToken has a balance, * the function attempts to redeem it. This helps maintain the list by clearing out matured repoTokens and redeeming their balances. */ function removeAndRedeemMaturedTokens( RepoTokenListData storage listData ) internal { if (listData.head == NULL_NODE) return; address current = listData.head; address prev = current; while (current != NULL_NODE) { address next; if (getRepoTokenMaturity(current) <= block.timestamp) { bool removeMaturedToken; uint256 repoTokenBalance = ITermRepoToken(current).balanceOf( address(this) ); if (repoTokenBalance > 0) { (, , address termRepoServicer, ) = ITermRepoToken(current) .config(); try ITermRepoServicer(termRepoServicer) .redeemTermRepoTokens( address(this), repoTokenBalance ) { removeMaturedToken = true; } catch { // redemption failed, do not remove token from the list } } else { // already redeemed removeMaturedToken = true; } next = _getNext(listData, current); if (removeMaturedToken) { if (current == listData.head) { listData.head = next; } listData.nodes[prev].next = next; delete listData.nodes[current]; delete listData.discountRates[current]; } } else { /// @dev early exit because list is sorted break; } prev = current; current = next; } } /** * @notice Validates a repoToken against specific criteria * @param listData The list data * @param repoToken The repoToken to validate * @param asset The address of the base asset * @return isRepoTokenValid Whether the repoToken is valid * @return redemptionTimestamp The redemption timestamp of the validated repoToken * * @dev Ensures the repoToken is deployed, matches the purchase token, is not matured, and meets collateral requirements. * Reverts with `InvalidRepoToken` if any validation check fails. */ function validateRepoToken( RepoTokenListData storage listData, ITermRepoToken repoToken, address asset ) internal view returns (bool isRepoTokenValid, uint256 redemptionTimestamp) { // Retrieve repo token configuration address purchaseToken; address collateralManager; (redemptionTimestamp, purchaseToken, , collateralManager) = repoToken .config(); // Validate purchase token if (purchaseToken != asset) { return (false, redemptionTimestamp); } // Check if repo token has matured if (redemptionTimestamp < block.timestamp) { return (false, redemptionTimestamp); } // Validate collateral token ratios uint256 numTokens = ITermRepoCollateralManager(collateralManager) .numOfAcceptedCollateralTokens(); for (uint256 i; i < numTokens; i++) { address currentToken = ITermRepoCollateralManager(collateralManager) .collateralTokens(i); uint256 minCollateralRatio = listData.collateralTokenParams[ currentToken ]; if (minCollateralRatio == 0) { return (false, redemptionTimestamp); } else if ( ITermRepoCollateralManager(collateralManager) .maintenanceCollateralRatios(currentToken) < minCollateralRatio ) { return (false, redemptionTimestamp); } } return (true, redemptionTimestamp); } /** * @notice Validate and insert a repoToken into the list data * @param listData The list data * @param repoToken The repoToken to validate and insert * @param discountRateAdapter The discount rate adapter * @param asset The address of the base asset * @return validRepoToken Whether the repoToken is valid * @return redemptionTimestamp The redemption timestamp of the validated repoToken */ function validateAndInsertRepoToken( RepoTokenListData storage listData, ITermRepoToken repoToken, ITermDiscountRateAdapter discountRateAdapter, address asset ) internal returns (bool validRepoToken, uint256 redemptionTimestamp) { uint256 discountRate = listData.discountRates[address(repoToken)]; if (discountRate != INVALID_AUCTION_RATE) { (redemptionTimestamp, , , ) = repoToken.config(); // skip matured repoTokens if (redemptionTimestamp < block.timestamp) { return (false, redemptionTimestamp); //revert InvalidRepoToken(address(repoToken)); } uint256 oracleRate; try discountRateAdapter.getDiscountRate(address(repoToken)) returns (uint256 rate) { oracleRate = rate; } catch {} if (oracleRate != 0) { if (discountRate != oracleRate) { listData.discountRates[address(repoToken)] = oracleRate; } } } else { try discountRateAdapter.getDiscountRate(address(repoToken)) returns (uint256 rate) { discountRate = rate == 0 ? ZERO_AUCTION_RATE : rate; } catch { discountRate = INVALID_AUCTION_RATE; return (false, redemptionTimestamp); } bool isRepoTokenValid; (isRepoTokenValid, redemptionTimestamp) = validateRepoToken( listData, repoToken, asset ); if (!isRepoTokenValid) { return (false, redemptionTimestamp); } insertSorted(listData, address(repoToken)); listData.discountRates[address(repoToken)] = discountRate; } return (true, redemptionTimestamp); } /** * @notice Insert a repoToken into the list in a sorted manner * @param listData The list data * @param repoToken The address of the repoToken to be inserted * * @dev Inserts the `repoToken` into the `listData` while maintaining the list sorted by the repoTokens' maturity timestamps. * The function iterates through the list to find the correct position for the new `repoToken` and updates the pointers accordingly. */ function insertSorted( RepoTokenListData storage listData, address repoToken ) internal { // Start at the head of the list address current = listData.head; // If the list is empty, set the new repoToken as the head if (current == NULL_NODE) { listData.head = repoToken; listData.nodes[repoToken].next = NULL_NODE; return; } uint256 maturityToInsert = getRepoTokenMaturity(repoToken); address prev; while (current != NULL_NODE) { // If the repoToken is already in the list, exit if (current == repoToken) { break; } uint256 currentMaturity = getRepoTokenMaturity(current); // Insert repoToken before current if its maturity is less than current maturity if (maturityToInsert < currentMaturity) { if (prev == NULL_NODE) { listData.head = repoToken; } else { listData.nodes[prev].next = repoToken; } listData.nodes[repoToken].next = current; break; } // Move to the next node address next = _getNext(listData, current); // If at the end of the list, insert repoToken after current if (next == NULL_NODE) { listData.nodes[current].next = repoToken; listData.nodes[repoToken].next = NULL_NODE; break; } prev = current; current = next; } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ITermRepoToken} from "./interfaces/term/ITermRepoToken.sol"; /*////////////////////////////////////////////////////////////// LIBRARY: RepoTokenUtils //////////////////////////////////////////////////////////////*/ library RepoTokenUtils { uint256 internal constant THREESIXTY_DAYCOUNT_SECONDS = 360 days; uint256 internal constant RATE_PRECISION = 1e18; /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Calculate the present value of a repoToken * @param repoTokenAmountInBaseAssetPrecision The amount of repoToken in base asset precision * @param purchaseTokenPrecision The precision of the purchase token * @param redemptionTimestamp The redemption timestamp of the repoToken * @param discountRate The auction rate * @return presentValue The present value of the repoToken */ function calculatePresentValue( uint256 repoTokenAmountInBaseAssetPrecision, uint256 purchaseTokenPrecision, uint256 redemptionTimestamp, uint256 discountRate ) internal view returns (uint256 presentValue) { uint256 timeLeftToMaturityDayFraction = block.timestamp > redemptionTimestamp ? 0 : ((redemptionTimestamp - block.timestamp) * purchaseTokenPrecision) / THREESIXTY_DAYCOUNT_SECONDS; // repoTokenAmountInBaseAssetPrecision / (1 + r * days / 360) presentValue = (repoTokenAmountInBaseAssetPrecision * purchaseTokenPrecision) / (purchaseTokenPrecision + ((discountRate * timeLeftToMaturityDayFraction) / RATE_PRECISION)); return presentValue > repoTokenAmountInBaseAssetPrecision ? repoTokenAmountInBaseAssetPrecision : presentValue; } /** * @notice Get the normalized amount of a repoToken in base asset precision * @param repoToken The address of the repoToken * @param repoTokenAmount The amount of the repoToken * @param purchaseTokenPrecision The precision of the purchase token * @param repoRedemptionHaircut The haircut to be applied to the repoToken for bad debt * @return repoTokenAmountInBaseAssetPrecision The normalized amount of the repoToken in base asset precision */ function getNormalizedRepoTokenAmount( address repoToken, uint256 repoTokenAmount, uint256 purchaseTokenPrecision, uint256 repoRedemptionHaircut ) internal view returns (uint256 repoTokenAmountInBaseAssetPrecision) { uint256 repoTokenPrecision = 10 ** ERC20(repoToken).decimals(); uint256 redemptionValue = ITermRepoToken(repoToken).redemptionValue(); repoTokenAmountInBaseAssetPrecision = repoRedemptionHaircut != 0 ? (redemptionValue * repoRedemptionHaircut * repoTokenAmount * purchaseTokenPrecision) / (repoTokenPrecision * RATE_PRECISION * 1e18) : (redemptionValue * repoTokenAmount * purchaseTokenPrecision) / (repoTokenPrecision * RATE_PRECISION); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; import {ITermController} from "./interfaces/term/ITermController.sol"; import {ITermAuction} from "./interfaces/term/ITermAuction.sol"; import {ITermAuctionOfferLocker} from "./interfaces/term/ITermAuctionOfferLocker.sol"; import {ITermRepoToken} from "./interfaces/term/ITermRepoToken.sol"; import {ITermRepoServicer} from "./interfaces/term/ITermRepoServicer.sol"; import {ITermDiscountRateAdapter} from "./interfaces/term/ITermDiscountRateAdapter.sol"; import {RepoTokenList, RepoTokenListData} from "./RepoTokenList.sol"; import {RepoTokenUtils} from "./RepoTokenUtils.sol"; // In-storage representation of an offer object struct PendingOffer { address repoToken; uint256 offerAmount; ITermAuction termAuction; ITermAuctionOfferLocker offerLocker; } struct TermAuctionListNode { bytes32 next; } struct TermAuctionListData { bytes32 head; mapping(bytes32 => TermAuctionListNode) nodes; mapping(bytes32 => PendingOffer) offers; } /*////////////////////////////////////////////////////////////// LIBRARY: TermAuctionList //////////////////////////////////////////////////////////////*/ library TermAuctionList { using RepoTokenList for RepoTokenListData; bytes32 internal constant NULL_NODE = bytes32(0); /*////////////////////////////////////////////////////////////// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @dev Get the next node in the list * @param listData The list data * @param current The current node * @return The next node */ function _getNext( TermAuctionListData storage listData, bytes32 current ) private view returns (bytes32) { return listData.nodes[current].next; } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Count the number of nodes in the list * @param listData The list data * @return count The number of nodes in the list */ function _count( TermAuctionListData storage listData ) internal view returns (uint256 count) { if (listData.head == NULL_NODE) return 0; bytes32 current = listData.head; while (current != NULL_NODE) { count++; current = _getNext(listData, current); } } /** * @notice Retrieves an array of offer IDs representing the pending offers * @param listData The list data * @return offers An array of offer IDs representing the pending offers * * @dev This function iterates through the list of offers and gathers their IDs into an array of `bytes32`. * This makes it easier to process and manage the pending offers. */ function pendingOffers( TermAuctionListData storage listData ) internal view returns (bytes32[] memory offers) { uint256 count = _count(listData); if (count > 0) { offers = new bytes32[](count); uint256 i; bytes32 current = listData.head; while (current != NULL_NODE) { offers[i++] = current; current = _getNext(listData, current); } } } /** * @notice Inserts a new pending offer into the list data * @param listData The list data * @param offerId The ID of the offer to be inserted * @param pendingOffer The `PendingOffer` struct containing details of the offer to be inserted * * @dev This function inserts a new pending offer while maintaining the list sorted by auction address. * The function iterates through the list to find the correct position for the new `offerId` and updates the pointers accordingly. */ function insertPending( TermAuctionListData storage listData, bytes32 offerId, PendingOffer memory pendingOffer ) internal { bytes32 current = listData.head; require(!pendingOffer.termAuction.auctionCompleted()); // If the list is empty, set the new repoToken as the head if (current == NULL_NODE) { listData.head = offerId; listData.nodes[offerId].next = NULL_NODE; listData.offers[offerId] = pendingOffer; return; } bytes32 prev; while (current != NULL_NODE) { // If the offerId is already in the list, exit if (current == offerId) { break; } address currentAuction = address( listData.offers[current].termAuction ); address auctionToInsert = address(pendingOffer.termAuction); // Insert offer before current if the auction address to insert is less than current auction address if (auctionToInsert < currentAuction) { if (prev == NULL_NODE) { listData.head = offerId; } else { listData.nodes[prev].next = offerId; } listData.nodes[offerId].next = current; break; } // Move to the next node bytes32 next = _getNext(listData, current); // If at the end of the list, insert repoToken after current if (next == NULL_NODE) { listData.nodes[current].next = offerId; listData.nodes[offerId].next = NULL_NODE; break; } prev = current; current = next; } listData.offers[offerId] = pendingOffer; } /** * @notice Removes completed or cancelled offers from the list data and processes the corresponding repoTokens * @param listData The list data * @param repoTokenListData The repoToken list data * @param discountRateAdapter The discount rate adapter * @param asset The address of the asset * * @dev This function iterates through the list of offers and removes those that are completed or cancelled. * It processes the corresponding repoTokens by validating and inserting them if necessary. This helps maintain * the list by clearing out inactive offers and ensuring repoTokens are correctly processed. */ function removeCompleted( TermAuctionListData storage listData, RepoTokenListData storage repoTokenListData, ITermDiscountRateAdapter discountRateAdapter, address asset ) internal { // Return if the list is empty if (listData.head == NULL_NODE) return; bytes32 current = listData.head; bytes32 prev = current; while (current != NULL_NODE) { PendingOffer memory offer = listData.offers[current]; bytes32 next = _getNext(listData, current); uint256 offerAmount = offer.offerLocker.lockedOffer(current).amount; bool removeNode; if (offer.termAuction.auctionCompleted()) { // If auction is completed and closed, mark for removal and prepare to insert repo token removeNode = true; // Auction still open => include offerAmount in totalValue // (otherwise locked purchaseToken will be missing from TV) // Auction completed but not closed => include offer.offerAmount in totalValue // because the offerLocker will have already removed the offer. // This applies if the repoToken hasn't been added to the repoTokenList // (only for new auctions, not reopenings). ( bool isValidRepoToken, uint256 redemptionTimestamp ) = repoTokenListData.validateAndInsertRepoToken( ITermRepoToken(offer.repoToken), discountRateAdapter, asset ); if ( !isValidRepoToken && block.timestamp > redemptionTimestamp ) { ITermRepoToken repoToken = ITermRepoToken(offer.repoToken); (, , address repoServicerAddr, ) = repoToken.config(); ITermRepoServicer repoServicer = ITermRepoServicer( repoServicerAddr ); try repoServicer.redeemTermRepoTokens( address(this), repoToken.balanceOf(address(this)) ) {} catch {} } } else { if (offer.termAuction.auctionCancelledForWithdrawal()) { // If auction was canceled for withdrawal, remove the node and unlock offers manually bytes32[] memory offerIds = new bytes32[](1); offerIds[0] = current; try offer.offerLocker.unlockOffers(offerIds) { // unlocking offer in this scenario withdraws offer amount removeNode = true; } catch { removeNode = false; } } else { if (offerAmount == 0) { // If offer amount is zero, it indicates the auction was canceled or deleted removeNode = true; } } } if (removeNode) { // Update the list to remove the current node delete listData.nodes[current]; delete listData.offers[current]; if (current == listData.head) { listData.head = next; } else { listData.nodes[prev].next = next; current = prev; } } // Move to the next node prev = current; current = next; } } /** * @notice Calculates the total present value of all relevant offers related to a specified repoToken * @param listData The list data * @param repoTokenListData The repoToken list data * @param discountRateAdapter The discount rate adapter * @param purchaseTokenPrecision The precision of the purchase token * @param repoTokenToMatch The address of the repoToken to match (optional) * @return totalValue The total present value of the offers * * @dev This function calculates the present value of offers in the list. If `repoTokenToMatch` is provided, * it will filter the calculations to include only the specified repoToken. If `repoTokenToMatch` is not provided, * it will aggregate the present value of all repoTokens in the list. This provides flexibility for both aggregate * and specific token evaluations. */ function getPresentValue( TermAuctionListData storage listData, RepoTokenListData storage repoTokenListData, ITermDiscountRateAdapter discountRateAdapter, uint256 purchaseTokenPrecision, address repoTokenToMatch ) internal view returns (uint256 totalValue) { // Return 0 if the list is empty if (listData.head == NULL_NODE) return 0; address edgeCaseAuction; // NOTE: handle edge case, assumes that pendingOffer is properly sorted by auction address bytes32 current = listData.head; while (current != NULL_NODE) { PendingOffer storage offer = listData.offers[current]; // Filter by specific repo token if provided, address(0) bypasses this filter if ( repoTokenToMatch != address(0) && offer.repoToken != repoTokenToMatch ) { // Not a match, skip // Move to the next token in the list current = _getNext(listData, current); continue; } uint256 offerAmount = offer.offerLocker.lockedOffer(current).amount; // Handle new or unseen repo tokens /// @dev offer processed, but auctionClosed not yet called and auction is new so repoToken not on List and wont be picked up /// checking repoTokendiscountRates to make sure we are not double counting on re-openings if ( offer.termAuction.auctionCompleted() && repoTokenListData.discountRates[offer.repoToken] == 0 ) { if (edgeCaseAuction != address(offer.termAuction)) { uint256 repoTokenAmountInBaseAssetPrecision = RepoTokenUtils .getNormalizedRepoTokenAmount( offer.repoToken, ITermRepoToken(offer.repoToken).balanceOf( address(this) ), purchaseTokenPrecision, discountRateAdapter.repoRedemptionHaircut( offer.repoToken ) ); totalValue += RepoTokenUtils.calculatePresentValue( repoTokenAmountInBaseAssetPrecision, purchaseTokenPrecision, RepoTokenList.getRepoTokenMaturity(offer.repoToken), discountRateAdapter.getDiscountRate(offer.repoToken) ); // Mark the edge case auction as processed to avoid double counting // since multiple offers can be tied to the same auction, we need to mark // the edge case auction as processed to avoid double counting edgeCaseAuction = address(offer.termAuction); } } else { // Add the offer amount to the total value totalValue += offerAmount; } // Move to the next token in the list current = _getNext(listData, current); } } /** * @notice Get cumulative offer data for a specified repoToken * @param listData The list data * @param repoTokenListData The repoToken list data * @param discountRateAdapter The discount rate adapter * @param repoToken The address of the repoToken (optional) * @param newOfferAmount The new offer amount for the specified repoToken * @param purchaseTokenPrecision The precision of the purchase token * @return cumulativeWeightedTimeToMaturity The cumulative weighted time to maturity * @return cumulativeOfferAmount The cumulative repoToken amount * @return found Whether the specified repoToken was found in the list * * @dev This function calculates cumulative data for all offers in the list. The `repoToken` and `newOfferAmount` * parameters are optional and provide flexibility to include the newOfferAmount for a specified repoToken in the calculation. * If `repoToken` is set to `address(0)` or `newOfferAmount` is `0`, the function calculates the cumulative data * without adjustments. */ function getCumulativeOfferData( TermAuctionListData storage listData, RepoTokenListData storage repoTokenListData, ITermDiscountRateAdapter discountRateAdapter, address repoToken, uint256 newOfferAmount, uint256 purchaseTokenPrecision ) internal view returns ( uint256 cumulativeWeightedTimeToMaturity, uint256 cumulativeOfferAmount, bool found ) { // If the list is empty, return 0s and false if (listData.head == NULL_NODE) return (0, 0, false); address edgeCaseAuction; // NOTE: handle edge case, assumes that pendingOffer is properly sorted by auction address bytes32 current = listData.head; while (current != NULL_NODE) { PendingOffer storage offer = listData.offers[current]; uint256 offerAmount; if (offer.repoToken == repoToken) { offerAmount = newOfferAmount; found = true; } else { // Retrieve the current offer amount from the offer locker offerAmount = offer.offerLocker.lockedOffer(current).amount; // Handle new repo tokens or reopening auctions /// @dev offer processed, but auctionClosed not yet called and auction is new so repoToken not on List and wont be picked up /// checking repoTokendiscountRates to make sure we are not double counting on re-openings if ( offer.termAuction.auctionCompleted() && repoTokenListData.discountRates[offer.repoToken] == 0 ) { // use normalized repoToken amount if repoToken is not in the list if (edgeCaseAuction != address(offer.termAuction)) { offerAmount = RepoTokenUtils .getNormalizedRepoTokenAmount( offer.repoToken, ITermRepoToken(offer.repoToken).balanceOf( address(this) ), purchaseTokenPrecision, discountRateAdapter.repoRedemptionHaircut( offer.repoToken ) ); // Mark the edge case auction as processed to avoid double counting // since multiple offers can be tied to the same auction, we need to mark // the edge case auction as processed to avoid double counting edgeCaseAuction = address(offer.termAuction); } } } if (offerAmount > 0) { // Calculate weighted time to maturity uint256 weightedTimeToMaturity = RepoTokenList .getRepoTokenWeightedTimeToMaturity( offer.repoToken, offerAmount ); cumulativeWeightedTimeToMaturity += weightedTimeToMaturity; cumulativeOfferAmount += offerAmount; } // Move to the next token in the list current = _getNext(listData, current); } } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"components":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_yearnVault","type":"address"},{"internalType":"address","name":"_discountRateAdapter","type":"address"},{"internalType":"address","name":"_eventEmitter","type":"address"},{"internalType":"address","name":"_governorAddress","type":"address"},{"internalType":"address","name":"_termController","type":"address"},{"internalType":"uint256","name":"_repoTokenConcentrationLimit","type":"uint256"},{"internalType":"uint256","name":"_timeToMaturityThreshold","type":"uint256"},{"internalType":"uint256","name":"_requiredReserveRatio","type":"uint256"},{"internalType":"uint256","name":"_discountRateMarkup","type":"uint256"}],"internalType":"struct Strategy.StrategyParams","name":"_params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AuctionNotOpen","type":"error"},{"inputs":[],"name":"BalanceBelowRequiredReserveRatio","type":"error"},{"inputs":[],"name":"DepositPaused","type":"error"},{"inputs":[{"internalType":"uint256","name":"have","type":"uint256"},{"internalType":"uint256","name":"want","type":"uint256"}],"name":"InsufficientLiquidBalance","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"InvalidRepoToken","type":"error"},{"inputs":[{"internalType":"address","name":"auction","type":"address"}],"name":"InvalidTermAuction","type":"error"},{"inputs":[],"name":"OfferNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"}],"name":"RepoTokenBlacklisted","type":"error"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"}],"name":"RepoTokenConcentrationTooHigh","type":"error"},{"inputs":[],"name":"TimeToMaturityAboveThreshold","type":"error"},{"inputs":[],"name":"ZeroPurchaseTokenAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"auctionClosed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableWithdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"},{"internalType":"uint256","name":"discountRate","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculateRepoTokenPresentValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"termAuction","type":"address"},{"internalType":"bytes32[]","name":"offerIds","type":"bytes32[]"}],"name":"deleteAuctionOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"freeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"}],"name":"getRepoTokenConcentrationRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"}],"name":"getRepoTokenHoldingValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvestAndReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOffers","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"repoTokenBlacklist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"repoTokenHoldings","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"},{"internalType":"uint256","name":"repoTokenAmount","type":"uint256"}],"name":"sellRepoToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddr","type":"address"},{"internalType":"uint256","name":"minCollateralRatio","type":"uint256"}],"name":"setCollateralTokenParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdapter","type":"address"}],"name":"setDiscountRateAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newDiscountRateMarkup","type":"uint256"}],"name":"setDiscountRateMarkup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newGovernor","type":"address"}],"name":"setPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"},{"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"setRepoTokenBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRepoTokenConcentrationLimit","type":"uint256"}],"name":"setRepoTokenConcentrationLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRequiredReserveRatio","type":"uint256"}],"name":"setRequiredReserveRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTermControllerAddr","type":"address"}],"name":"setTermController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTimeToMaturityThreshold","type":"uint256"}],"name":"setTimeToMaturityThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"shutdownWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"repoToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"simulateTransaction","outputs":[{"internalType":"uint256","name":"simulatedWeightedMaturity","type":"uint256"},{"internalType":"uint256","name":"simulatedRepoTokenConcentrationRatio","type":"uint256"},{"internalType":"uint256","name":"simulatedLiquidityRatio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyState","outputs":[{"internalType":"address","name":"assetVault","type":"address"},{"internalType":"address","name":"eventEmitter","type":"address"},{"internalType":"address","name":"governorAddress","type":"address"},{"internalType":"contract ITermController","name":"prevTermController","type":"address"},{"internalType":"contract ITermController","name":"currTermController","type":"address"},{"internalType":"contract ITermDiscountRateAdapter","name":"discountRateAdapter","type":"address"},{"internalType":"uint256","name":"timeToMaturityThreshold","type":"uint256"},{"internalType":"uint256","name":"requiredReserveRatio","type":"uint256"},{"internalType":"uint256","name":"discountRateMarkup","type":"uint256"},{"internalType":"uint256","name":"repoTokenConcentrationLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ITermAuction","name":"termAuction","type":"address"},{"internalType":"address","name":"repoToken","type":"address"},{"internalType":"bytes32","name":"idHash","type":"bytes32"},{"internalType":"bytes32","name":"offerPriceHash","type":"bytes32"},{"internalType":"uint256","name":"purchaseTokenAmount","type":"uint256"}],"name":"submitAuctionOffer","outputs":[{"internalType":"bytes32[]","name":"offerIds","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_totalIdle","type":"uint256"}],"name":"tendThis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tendTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenizedStrategyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssetValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpauseDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101206040523480156200001257600080fd5b5060405162006eb038038062006eb08339810160408190526200003591620008d7565b80516001600160a01b0381166080523060a05260405184906200009e906200006a908490849033908190819060240162000a32565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b17909152620002c616565b505073bb51273d6c746910c7c06fe718f30c936170fed07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55506000805460ff191690556020808201516001600160a01b03908116610100526060830151811660c0526080516040805163313ce56760e01b81529051919092169263313ce56792600480820193918290030181865afa15801562000140573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000166919062000a75565b6200017390600a62000bad565b60e0526020810151815162000197916001600160a01b03909116906000196200035a565b600a620001a5838262000c4e565b50604080516101408101825261010080516001600160a01b0390811680845260c08051831660208601819052608080890180518616888a01819052600060608a015260a0808c01518816938a01849052998b015190961698880189905260e0808b0151858a018190528b890151918a01829052610120808d0151998b018a9052958c015195909901859052600b80546001600160a01b03199081169097179055600c80548716909417909355600d80548616909617909555600e805485169055600f8054851690911790556010805490931690961790915560119390935560129390935560135560145551620002bd907f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5590620004ad565b50505062000d8b565b606060008073bb51273d6c746910c7c06fe718f30c936170fed06001600160a01b031684604051620002f9919062000d1a565b600060405180830381855af49150503d806000811462000336576040519150601f19603f3d011682016040523d82523d6000602084013e6200033b565b606091505b50915091508162000353576040513d806000833e8082fd5b9392505050565b801580620003d85750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015620003b0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003d6919062000d38565b155b620004505760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084015b60405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620004a89185916200051c16565b505050565b620004b98282620005f0565b620005185760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45b5050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200056b906001600160a01b0385169084906200061d565b90508051600014806200058f5750808060200190518101906200058f919062000d52565b620004a85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000447565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b60606200062e848460008562000636565b949350505050565b606082471015620006995760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000447565b600080866001600160a01b03168587604051620006b7919062000d1a565b60006040518083038185875af1925050503d8060008114620006f6576040519150601f19603f3d011682016040523d82523d6000602084013e620006fb565b606091505b5090925090506200070f878383876200071a565b979650505050505050565b606083156200078e57825160000362000786576001600160a01b0385163b620007865760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000447565b50816200062e565b6200062e8383815115620007a55781518083602001fd5b8060405162461bcd60e51b815260040162000447919062000d76565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b0381118282101715620007fd57620007fd620007c1565b60405290565b60005b838110156200082057818101518382015260200162000806565b50506000910152565b600082601f8301126200083b57600080fd5b81516001600160401b0380821115620008585762000858620007c1565b604051601f8301601f19908116603f01168101908282118183101715620008835762000883620007c1565b816040528381528660208588010111156200089d57600080fd5b620008b084602083016020890162000803565b9695505050505050565b80516001600160a01b0381168114620008d257600080fd5b919050565b6000806000838503610180811215620008ef57600080fd5b84516001600160401b03808211156200090757600080fd5b620009158883890162000829565b955060208701519150808211156200092c57600080fd5b506200093b8782880162000829565b93505061014080603f19830112156200095357600080fd5b6200095d620007d7565b91506200096d60408701620008ba565b82526200097d60608701620008ba565b60208301526200099060808701620008ba565b6040830152620009a360a08701620008ba565b6060830152620009b660c08701620008ba565b6080830152620009c960e08701620008ba565b60a08301526101008681015160c08401526101208088015160e085015291870151908301526101609095015194810194909452509093909250565b6000815180845262000a1e81602086016020860162000803565b601f01601f19169290920160200192915050565b600060018060a01b03808816835260a0602084015262000a5660a084018862000a04565b9581166040840152938416606083015250911660809091015292915050565b60006020828403121562000a8857600080fd5b815160ff811681146200035357600080fd5b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000af157816000190482111562000ad55762000ad562000a9a565b8085161562000ae357918102915b93841c939080029062000ab5565b509250929050565b60008262000b0a5750600162000617565b8162000b195750600062000617565b816001811462000b32576002811462000b3d5762000b5d565b600191505062000617565b60ff84111562000b515762000b5162000a9a565b50506001821b62000617565b5060208310610133831016604e8410600b841016171562000b82575081810a62000617565b62000b8e838362000ab0565b806000190482111562000ba55762000ba562000a9a565b029392505050565b60006200035360ff84168362000af9565b600181811c9082168062000bd357607f821691505b60208210810362000bf457634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004a8576000816000526020600020601f850160051c8101602086101562000c255750805b601f850160051c820191505b8181101562000c465782815560010162000c31565b505050505050565b81516001600160401b0381111562000c6a5762000c6a620007c1565b62000c828162000c7b845462000bbe565b8462000bfa565b602080601f83116001811462000cba576000841562000ca15750858301515b600019600386901b1c1916600185901b17855562000c46565b600085815260208120601f198616915b8281101562000ceb5788860151825594840194600190910190840162000cca565b508582101562000d0a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000825162000d2e81846020870162000803565b9190910192915050565b60006020828403121562000d4b57600080fd5b5051919050565b60006020828403121562000d6557600080fd5b815180151581146200035357600080fd5b60208152600062000353602083018462000a04565b60805160a05160c05160e05161010051615fdd62000ed36000396000818161262a01528181612c4501528181613591015261470a0152600081816108d10152818161094d0152818161151b015281816115480152818161183c015281816119e601528181611a2001528181612775015281816127af01528181612a0a01528181612a6f01528181612b5101528181612d990152612dd8015260008181610b2701528181610f710152818161101a015281816110b50152818161116101528181611a8901528181611c4701528181611d9e01528181611eb50152818161201001526120b8015260008181610d1b01526111db0152600081816107a401528181610a7201528181610e1401528181611352015281816113de0152818161254a01528181612590015281816126d4015281816130b0015281816133ec01526135bb0152615fdd6000f3fe608060405234801561001057600080fd5b50600436106102945760003560e01c806372db6dfc11610167578063aeaaa299116100ce578063e3bafad211610087578063e3bafad2146106ad578063e58bb639146106c0578063ed22088e146106c8578063f235757f146106db578063fb06a3bd146106ee578063fde813a8146105f057610294565b8063aeaaa29914610631578063aff518f314610639578063b7bd869c1461064c578063d19a3bb81461065f578063d547741f14610692578063d8c143f7146106a557610294565b806391d148541161012057806391d14854146105c857806395d89b41146105db5780639d7fb70c146105f0578063a217fddf14610603578063ad5681591461060b578063add5ba4b1461061e57610294565b806372db6dfc1461054f578063740152831461056257806383076af9146105905780638c02ea3a146105a55780638f90ae5a146105b8578063903c2c06146105c057610294565b806346aa2f121161020b5780635c975abb116101c45780635c975abb146104e35780635d265d3f146104ee57806367747e571461052457806369026e88146105375780636bc912e01461053f578063702848131461054757610294565b806346aa2f121461047557806349317f1d1461048a578063503160d9146104925780635134f576146104a55780635157ced5146104b857806354c885e0146104c057610294565b8063176eacd31161025d578063176eacd3146103e5578063248a9ca3146103f85780632f2ff15d1461041c57806336568abe1461042f5780633d6cb57514610442578063442e61aa1461045557610294565b80625446f2146102cf57806301ffc9a7146102e257806304bd46291461030a57806311b4e2531461032b57806312345134146103d2575b73bb51273d6c746910c7c06fe718f30c936170fed03660008037600080366000845af43d6000803e8080156102c8573d6000f35b3d6000fd5b005b6102cd6102dd366004615704565b6106f6565b6102f56102f0366004615730565b610aa7565b60405190151581526020015b60405180910390f35b61031d61031836600461575a565b610ade565b604051908152602001610301565b600b54600c54600d54600e54600f5460105460115460125460135460145461036d996001600160a01b039081169981169881169781169681169516939291908a565b604080516001600160a01b039b8c168152998b1660208b0152978a169789019790975294881660608801529287166080870152951660a085015260c084019490945260e083019390935261010082019290925261012081019190915261014001610301565b6102cd6103e0366004615785565b610ae8565b61031d6103f336600461575a565b610baf565b61031d6104063660046157be565b6000908152600160208190526040909120015490565b6102cd61042a3660046157d7565b610bf8565b6102cd61043d3660046157d7565b610c23565b6102cd6104503660046157be565b610ca1565b6104686104633660046157fc565b610cb5565b604051610301919061584d565b61031d61048336600461575a565b5060001990565b61031d610f10565b6102cd6104a03660046157be565b610f27565b6102cd6104b33660046157be565b610f38565b6102cd610fde565b6102f56104ce36600461575a565b60156020526000908152604090205460ff1681565b60005460ff166102f5565b60408051600481526024810182526020810180516001600160e01b031663440368a360e01b1790529051610301916000916158e1565b6102cd6105323660046157be565b61107c565b6102cd611122565b6104686111a8565b61031d6111b4565b6102cd61055d3660046158fc565b6111c6565b610575610570366004615704565b611380565b60408051938452602084019290925290820152606001610301565b61059861160a565b6040516103019190615984565b61031d6105b336600461575a565b611616565b61031d611872565b61031d61187f565b6102f56105d63660046157d7565b611889565b6105e36118b4565b60405161030191906159c5565b6102cd6105fe3660046157be565b611946565b61031d600081565b61031d6106193660046159d8565b61194e565b6102cd61062c3660046157be565b611a50565b6102cd611af6565b6102cd61064736600461575a565b611b02565b6102cd61065a36600461575a565b611cd7565b61067a73bb51273d6c746910c7c06fe718f30c936170fed081565b6040516001600160a01b039091168152602001610301565b6102cd6106a03660046157d7565b611e21565b6102cd611e47565b6102cd6106bb366004615704565b611e77565b6102cd611f2f565b6102cd6106d63660046157be565b61207f565b6102cd6106e936600461575a565b612125565b6102cd612179565b6106fe6121a6565b6001600160a01b038216600090815260156020526040902054829060ff161561074a57604051633d764d2b60e11b81526001600160a01b03821660048201526024015b60405180910390fd5b6000821161075757600080fd5b610760836121ec565b6107885760405163017dad6760e71b81526001600160a01b0384166004820152602401610741565b60105460009081906107c89060039087906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000061231b565b91509150816107f55760405163017dad6760e71b81526001600160a01b0386166004820152602401610741565b6107ff6000612532565b60006108096126b2565b90506000811161081857600080fd5b60006108238261275a565b90506000811161083257600080fd5b601054604051633580ec1360e11b81526001600160a01b0389811660048301526000921690636b01d82690602401602060405180830381865afa15801561087d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190615a0d565b601054604051631bf49af760e11b81526001600160a01b03808c166004830152929350600092610943928c928c927f00000000000000000000000000000000000000000000000000000000000000009216906337e935ee906024015b602060405180830381865afa15801561091a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093e9190615a0d565b6127e7565b90506000610983827f000000000000000000000000000000000000000000000000000000000000000088600b600801548761097e9190615a3c565b612956565b9050808510156109b057604051633bf1b8e760e11b81526004810186905260248101829052604401610741565b60006109c68b8b6109c1858a615a4f565b6129e6565b6011549091508111156109ec57604051632391f20360e21b815260040160405180910390fd5b6000856109f98489615a4f565b610a0b90670de0b6b3a7640000615a62565b610a159190615a79565b601254909150811015610a3b57604051631342c8d960e11b815260040160405180910390fd5b610a478c858886612be3565b610a5083612c22565b610a656001600160a01b038d1633308e612cba565b610a996001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163385612d2b565b505050505050505050505050565b60006001600160e01b03198216637965db0b60e01b1480610ad857506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000610ad86126b2565b600080516020615f88833981519152610b0081612d5b565b604051637948ecd560e01b81526001600160a01b03848116600483015283151560248301527f00000000000000000000000000000000000000000000000000000000000000001690637948ecd590604401600060405180830381600087803b158015610b6b57600080fd5b505af1158015610b7f573d6000803e3d6000fd5b505050506001600160a01b03929092166000908152601560205260409020805460ff191691151591909117905550565b60006001600160a01b038216610bdb5760405163017dad6760e71b815260006004820152602401610741565b610ad8826000610bf1610bec6126b2565b61275a565b6000612d65565b60008281526001602081905260409091200154610c1481612d5b565b610c1e8383612e3b565b505050565b6001600160a01b0381163314610c935760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610741565b610c9d8282612ea6565b5050565b610ca9612f0d565b610cb281612f44565b50565b6060610cbf6121a6565b6001600160a01b038516600090815260156020526040902054859060ff1615610d0657604051633d764d2b60e11b81526001600160a01b0382166004820152602401610741565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a6499060240160006040518083038186803b158015610d6557600080fd5b505afa158015610d79573d6000803e3d6000fd5b5050505082600003610d9e5760405163b97ce22960e01b815260040160405180910390fd5b6000610daa8888612f55565b9050610db66000612532565b6000868152600960205260409020600101548490610dfb6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b88815230602082015260408101889052606081018790527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166080820152610e4f8b85838d8787613255565b95506000610e5b6126b2565b90506000610e688261275a565b905060008111610e7757600080fd5b600081610e8c84670de0b6b3a7640000615a62565b610e969190615a79565b601254909150811015610ebc57604051631342c8d960e11b815260040160405180910390fd5b6000610eca600080866129e6565b601154909150811115610ef057604051632391f20360e21b815260040160405180910390fd5b610efe8e6000856000612be3565b50505050505050505095945050505050565b6000610f1a612f0d565b610f2261369c565b905090565b610f2f612f0d565b610cb2816136bb565b600080516020615f88833981519152610f5081612d5b565b601454604051632efd6ecd60e21b81526004810191909152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063bbf5bb3490604401600060405180830381600087803b158015610fbd57600080fd5b505af1158015610fd1573d6000803e3d6000fd5b5050506014929092555050565b600080516020615f88833981519152610ff681612d5b565b6002805460ff19169055604080516305ad0e1f60e51b815290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b5a1c3e091600480830192600092919082900301818387803b15801561106157600080fd5b505af1158015611075573d6000803e3d6000fd5b5050505050565b600080516020615f8883398151915261109481612d5b565b60125460405163627d8a0160e11b81526004810191909152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c4fb140290604401600060405180830381600087803b15801561110157600080fd5b505af1158015611115573d6000803e3d6000fd5b5050506012929092555050565b600080516020615f8883398151915261113a81612d5b565b6002805460ff1916600117905560408051631022cfd760e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631022cfd791600480830192600092919082900301818387803b15801561106157600080fd5b6060610f2260076136f1565b6000610f226111c16126b2565b6137ad565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a6499060240160006040518083038186803b15801561122557600080fd5b505afa158015611239573d6000803e3d6000fd5b50505050611246836121ec565b61126e5760405163f14a3cf960e01b81526001600160a01b0384166004820152602401610741565b60008390506000816001600160a01b031663505d799e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d79190615a9b565b60405163720e52c560e01b81529091506001600160a01b0382169063720e52c5906113089087908790600401615ab8565b600060405180830381600087803b15801561132257600080fd5b505af1158015611336573d6000803e3d6000fd5b50506010546113769250600791506003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006137e9565b6110756000612532565b60008060008061138e6126b2565b90506000806001600160a01b03881615611580576113ab886121ec565b6113d35760405163017dad6760e71b81526001600160a01b0389166004820152602401610741565b60008061140260038b7f0000000000000000000000000000000000000000000000000000000000000000613c65565b915091508161142f5760405163017dad6760e71b81526001600160a01b038b166004820152602401610741565b601054604051633580ec1360e11b81526001600160a01b038c811660048301526000921690636b01d82690602401602060405180830381865afa15801561147a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149e9190615a0d565b601054604051631bf49af760e11b81526001600160a01b038e81166004830152929350600092909116906337e935ee90602401602060405180830381865afa1580156114ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115129190615a0d565b90506115408c8c7f0000000000000000000000000000000000000000000000000000000000000000846127e7565b9550611579867f000000000000000000000000000000000000000000000000000000000000000085600b600801548661097e9190615a3c565b9450505050505b61158f88886109c18487615a4f565b95506001600160a01b038816156115b7576115b488836115ae8661275a565b84612d65565b94505b60006115c28461275a565b9050806000036115d557600094506115ff565b806115e08386615a4f565b6115f290670de0b6b3a7640000615a62565b6115fc9190615a79565b94505b505050509250925092565b6060610f226003613eb9565b6001600160a01b03811660009081526005602052604081205481901561182457600f5460405163e7e4b8db60e01b81526001600160a01b038581166004830152600092169063e7e4b8db90602401602060405180830381865afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a59190615af1565b156116bc5750600f546001600160a01b031661173d565b600e5460405163e7e4b8db60e01b81526001600160a01b0386811660048301529091169063e7e4b8db90602401602060405180830381865afa158015611706573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172a9190615af1565b1561173d5750600e546001600160a01b03165b60105460405163739d020f60e11b81526001600160a01b038381166004830152808716602483015261182092879291169063e73a041e90604401602060405180830381865afa158015611794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b89190615a0d565b6040516370a0823160e01b81523060048201526001600160a01b038816906370a0823190602401602060405180830381865afa1580156117fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106199190615a0d565b9150505b601054611861906007906003906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000087613f81565b61186b9082615a3c565b9392505050565b6000610f22610bec6126b2565b6000610f226126b2565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060600a80546118c390615b0e565b80601f01602080910402602001604051908101604052809291908181526020018280546118ef90615b0e565b801561193c5780601f106119115761010080835404028352916020019161193c565b820191906000526020600020905b81548152906001019060200180831161191f57829003601f168201915b5050505050905090565b610cb2612f0d565b600080846001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190615b42565b5050601054604051631bf49af760e11b81526001600160a01b03808a166004830152939450600093611a189350899288927f000000000000000000000000000000000000000000000000000000000000000092909116906337e935ee906024016108fd565b9050611a46817f00000000000000000000000000000000000000000000000000000000000000008488612956565b9695505050505050565b600080516020615f88833981519152611a6881612d5b565b6013546040516309209da160e01b81526004810191909152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906309209da190604401600060405180830381600087803b158015611ad557600080fd5b505af1158015611ae9573d6000803e3d6000fd5b5050506013929092555050565b611b006000612532565b565b600080516020615f88833981519152611b1a81612d5b565b6001600160a01b038216611b2d57600080fd5b60006001600160a01b0316826001600160a01b031663025f1a5e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9a9190615a9b565b6001600160a01b031603611bad57600080fd5b6003546001600160a01b03165b6001600160a01b03811615611c1a57611bd2816121ec565b611bfa5760405163017dad6760e71b81526001600160a01b0382166004820152602401610741565b6001600160a01b0390811660009081526004602052604090205416611bba565b600f546040516333d864dd60e21b81526001600160a01b03918216600482018190528583166024830152917f0000000000000000000000000000000000000000000000000000000000000000169063cf61937490604401600060405180830381600087803b158015611c8b57600080fd5b505af1158015611c9f573d6000803e3d6000fd5b5050600e80546001600160a01b039485166001600160a01b031991821617909155600f80549790941696169590951790915550505050565b600080516020615f88833981519152611cef81612d5b565b600082905060006001600160a01b0316816001600160a01b03166342a9d1316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d619190615a9b565b6001600160a01b031603611d7457600080fd5b6010546040516399146dff60e01b81526001600160a01b03918216600482015284821660248201527f0000000000000000000000000000000000000000000000000000000000000000909116906399146dff90604401600060405180830381600087803b158015611de457600080fd5b505af1158015611df8573d6000803e3d6000fd5b5050601080546001600160a01b0319166001600160a01b03949094169390931790925550505050565b60008281526001602081905260409091200154611e3d81612d5b565b610c1e8383612ea6565b600080516020615f88833981519152611e5f81612d5b565b611e676142a6565b506002805460ff19166001179055565b600080516020615f88833981519152611e8f81612d5b565b604051633759d6ad60e11b81526001600160a01b038481166004830152602482018490527f00000000000000000000000000000000000000000000000000000000000000001690636eb3ad5a90604401600060405180830381600087803b158015611ef957600080fd5b505af1158015611f0d573d6000803e3d6000fd5b505050506001600160a01b039290921660009081526006602052604090205550565b60025461010090046001600160a01b03163314611f815760405162461bcd60e51b815260206004820152601060248201526f10b832b73234b733a3b7bb32b93737b960811b6044820152606401610741565b600d54611fa690600080516020615f88833981519152906001600160a01b0316612ea6565b600254611fd090600080516020615f888339815191529061010090046001600160a01b0316612e3b565b600254600d80546101009092046001600160a01b039081166001600160a01b0319909316831790915560405163a12f83a960e01b815260048101929092527f0000000000000000000000000000000000000000000000000000000000000000169063a12f83a990602401600060405180830381600087803b15801561205457600080fd5b505af1158015612068573d6000803e3d6000fd5b505060028054610100600160a81b03191690555050565b600080516020615f8883398151915261209781612d5b565b6011546040516368e5255760e01b81526004810191909152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906368e5255790604401600060405180830381600087803b15801561210457600080fd5b505af1158015612118573d6000803e3d6000fd5b5050506011929092555050565b600080516020615f8883398151915261213d81612d5b565b6001600160a01b03821661215057600080fd5b50600280546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600080516020615f8883398151915261219181612d5b565b612199614300565b506002805460ff19169055565b60005460ff1615611b005760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610741565b600f54600e546000916001600160a01b0390811691168115801590612276575060405163e7e4b8db60e01b81526001600160a01b03858116600483015283169063e7e4b8db90602401602060405180830381865afa158015612252573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122769190615af1565b15612285575060019392505050565b6001600160a01b03811615801590612302575060405163e7e4b8db60e01b81526001600160a01b03858116600483015282169063e7e4b8db90602401602060405180830381865afa1580156122de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123029190615af1565b15612311575060019392505050565b5060009392505050565b6001600160a01b03831660009081526002850160205260408120548190801561245757856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa15801561237c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a09190615b42565b509193505050428210156123b8576000925050612529565b604051633580ec1360e11b81526001600160a01b03878116600483015260009190871690636b01d82690602401602060405180830381865afa92505050801561241e575060408051601f3d908101601f1916820190925261241b91810190615a0d565b60015b156124265790505b801561245157808214612451576001600160a01b038716600090815260028901602052604090208190555b50612523565b604051633580ec1360e11b81526001600160a01b038781166004830152861690636b01d82690602401602060405180830381865afa9250505080156124b9575060408051601f3d908101601f191682019092526124b691810190615a0d565b60015b6124c7575060009150612529565b80156124d357806124d6565b60015b91505060006124e6888887613c65565b93509050806124fa57600093505050612529565b6125048888614339565b506001600160a01b038616600090815260028801602052604090208190555b60019250505b94509492505050565b60105461256e906007906003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006137e9565b61257860036144cd565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156125df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126039190615a0d565b90508181111561269f57604051636e553f6560e01b815282820360048201523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303816000875af115801561267b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1e9190615a0d565b81811015610c9d57610c9d818303612c22565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561271b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273f9190615a0d565b90508061274a6146f2565b6127549190615a3c565b91505090565b60105460009061279a906007906003906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000085613f81565b6010546127d3906003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006147e4565b6127dd9084615a3c565b610ad89190615a3c565b600080856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612828573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284c9190615b97565b61285790600a615c9e565b90506000866001600160a01b031663ef4474cd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612899573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128bd9190615a0d565b9050836000036128fd576128d9670de0b6b3a764000083615a62565b856128e48884615a62565b6128ee9190615a62565b6128f89190615a79565b61294b565b61290f670de0b6b3a764000083615a62565b61292190670de0b6b3a7640000615a62565b858761292d8785615a62565b6129379190615a62565b6129419190615a62565b61294b9190615a79565b979650505050505050565b600080834211612989576301da9c00856129704287615a4f565b61297a9190615a62565b6129849190615a79565b61298c565b60005b9050670de0b6b3a76400006129a18285615a62565b6129ab9190615a79565b6129b59086615a3c565b6129bf8688615a62565b6129c99190615a79565b91508582116129d857816129da565b855b9150505b949350505050565b60105460009081908190819081908190612a2e906003906001600160a01b03168b8b7f0000000000000000000000000000000000000000000000000000000000000000614991565b91945092509050612a3f8386615a3c565b9450612a4b8285615a3c565b60105490945060009081908190612a93906007906003906001600160a01b03168f8f7f0000000000000000000000000000000000000000000000000000000000000000614af3565b91945092509050612aa48389615a3c565b9750612ab08288615a3c565b965083158015612abe575080155b8015612ad257506001600160a01b038c1615155b15612b9d57601054604051631bf49af760e11b81526001600160a01b038e8116600483015260009216906337e935ee90602401602060405180830381865afa158015612b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b469190615a0d565b90506000612b768e8e7f0000000000000000000000000000000000000000000000000000000000000000856127e7565b9050612b82818a615a3c565b9850612b8e8e82614db1565b612b98908b615a3c565b995050505b86158015612ba9575089155b15612bbf5760009850505050505050505061186b565b612bc98a88615a3c565b612bd39089615a79565b9c9b505050505050505050505050565b6000612bf185858585612d65565b60145490915081111561107557604051633cb36e3560e11b81526001600160a01b0386166004820152602401610741565b604051632d182be560e21b815260048101829052306024820181905260448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b460af94906064016020604051808303816000875af1158015612c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9d9190615a0d565b6040516001600160a01b0380851660248301528316604482015260648101829052612d259085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614de9565b50505050565b6040516001600160a01b038316602482015260448101829052610c1e90849063a9059cbb60e01b90606401612cee565b610cb28133614ebe565b60008084612d7287611616565b612d7c9190615a3c565b9050600083612d8b8787615a3c565b612d959190615a4f565b90507f0000000000000000000000000000000000000000000000000000000000000000612dca83670de0b6b3a7640000615a62565b612dd49190615a79565b91507f0000000000000000000000000000000000000000000000000000000000000000612e0982670de0b6b3a7640000615a62565b612e139190615a79565b90508015612e2e57806128ee83670de0b6b3a7640000615a62565b5060009695505050505050565b612e458282611889565b610c9d5760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b612eb08282611889565b15610c9d5760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b333014611b005760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606401610741565b612f4c6121a6565b610cb281612532565b6000612f60836121ec565b612f885760405163f14a3cf960e01b81526001600160a01b0384166004820152602401610741565b612f91826121ec565b612fb95760405163017dad6760e71b81526001600160a01b0383166004820152602401610741565b816001600160a01b031663cc5b6e4a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ff7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301b9190615a0d565b836001600160a01b031663cc5b6e4a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307d9190615a0d565b146130a65760405163017dad6760e71b81526001600160a01b0383166004820152602401610741565b60006130d46003847f0000000000000000000000000000000000000000000000000000000000000000613c65565b509050806131005760405163017dad6760e71b81526001600160a01b0384166004820152602401610741565b6000846001600160a01b031663505d799e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613140573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131649190615a9b565b9050806001600160a01b031663eb54f9ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131c89190615a0d565b421115806132375750806001600160a01b031663ba829d716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561320f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132339190615a0d565b4210155b156129de5760405163f046007760e01b815260040160405180910390fd5b60606000866001600160a01b031663357a77d26040518163ffffffff1660e01b8152600401602060405180830381865afa158015613297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bb9190615a9b565b60408051600180825281830190925291925060009190816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816132d6579050509050868160008151811061332757613327615cc3565b6020026020010181905250838511156134165783850360006133476126b2565b90508181101561337457604051633bf1b8e760e11b81526004810182905260248101839052604401610741565b61337d82612c22565b613413846001600160a01b0316635c4b440e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133e29190615a9b565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169084614f17565b50505b60405163db875b6960e01b81526001600160a01b0389169063db875b6990613442908490600401615cd9565b6000604051808303816000875af1158015613461573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134899190810190615d84565b925082516000036134ad57604051636df5846d60e01b815260040160405180910390fd5b8360000361352957613524836000815181106134cb576134cb615cc3565b60200260200101516040518060800160405280896001600160a01b031681526020018a6060015181526020018c6001600160a01b031681526020018b6001600160a01b0316815250600761502c9092919063ffffffff16565b61356c565b6000600760020160008560008151811061354557613545615cc3565b60200260200101518152602001908152602001600020905087606001518160010181905550505b83851015613690576040516370a0823160e01b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691636e553f65917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015613602573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136269190615a0d565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044016020604051808303816000875af115801561366a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061368e9190615a0d565b505b50509695505050505050565b60006136a66121a6565b6136b06000612532565b610f22610bec6126b2565b6136c36121a6565b60025460ff16156136e75760405163035edea360e41b815260040160405180910390fd5b610cb26000612532565b606060006136fe83615250565b905080156137a7578067ffffffffffffffff81111561371f5761371f615cad565b604051908082528060200260200182016040528015613748578160200160208202803683370190505b5083549092506000905b80156137a45780848361376481615e2a565b94508151811061377657613776615cc3565b60200260200101818152505061379d85826000908152600191909101602052604090205490565b9050613752565b50505b50919050565b6000806137b98361275a565b9050806000036137cc5750600092915050565b806137df84670de0b6b3a7640000615a62565b61186b9190615a79565b835415612d25578354805b8115613c5d576000828152600287810160209081526040808420815160808101835281546001600160a01b0390811682526001808401548387015295830154811682850152600390920154821660608201908152888752948c0190935281852054935191516372e3a5b160e01b8152600481018890529294929116906372e3a5b19060240160e060405180830381865afa158015613896573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ba9190615e43565b608001519050600083604001516001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613904573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139289190615af1565b15613aa9575082516001906000908190613945908c908c8c61231b565b915091508115801561395657508042115b15613aa2576000866000015190506000816001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa1580156139a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c89190615b42565b506040516370a0823160e01b815230600482018190529194508493506001600160a01b038085169350637e237e899291908716906370a0823190602401602060405180830381865afa158015613a22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a469190615a0d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015613a8c57600080fd5b505af1925050508015613a9d575060015b505050505b5050613bd5565b83604001516001600160a01b0316638dfea9e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015613aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0f9190615af1565b15613bc957604080516001808252818301909252600091602080830190803683370190505090508681600081518110613b4a57613b4a615cc3565b60200260200101818152505084606001516001600160a01b031663720e52c5826040518263ffffffff1660e01b8152600401613b86919061584d565b600060405180830381600087803b158015613ba057600080fd5b505af1925050508015613bb1575060015b613bbe5760009150613bc3565b600191505b50613bd5565b81600003613bd5575060015b8015613c505760008681526001808c01602090815260408084208490556002808f01909252832080546001600160a01b0319908116825592810193909355820180548216905560039091018054909116905589548603613c3757828a55613c50565b600085815260018b016020526040902083905593945084935b50909392506137f4915050565b505050505050565b600080600080856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa158015613ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ccd9190615b42565b929550909350909150506001600160a01b0380831690861614613cf557600093505050613eb1565b42831015613d0857600093505050613eb1565b6000816001600160a01b031663190e80126040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d6c9190615b97565b60ff16905060005b81811015613ea85760405163172c48c760e01b8152600481018290526000906001600160a01b0385169063172c48c790602401602060405180830381865afa158015613dc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613de89190615a9b565b6001600160a01b038116600090815260038c016020526040812054919250819003613e1c5760009750505050505050613eb1565b6040516320db25d560e01b81526001600160a01b0383811660048301528291908716906320db25d590602401602060405180830381865afa158015613e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e899190615a0d565b1015613e9e5760009750505050505050613eb1565b5050600101613d74565b50600194505050505b935093915050565b60606000613ec683615290565b905080156137a7578067ffffffffffffffff811115613ee757613ee7615cad565b604051908082528060200260200182016040528015613f10578160200160208202803683370190505b5083549092506000906001600160a01b03165b6001600160a01b038116156137a457808483613f3e81615e2a565b945081518110613f5057613f50615cc3565b60200260200101906001600160a01b031690816001600160a01b031681525050613f7a85826152e3565b9050613f23565b8454600090613f925750600061429d565b85546000905b801561429a57600081815260028901602052604090206001600160a01b03851615801590613fd3575080546001600160a01b03868116911614155b15613fef57506000908152600188016020526040902054613f98565b60038101546040516372e3a5b160e01b8152600481018490526000916001600160a01b0316906372e3a5b19060240160e060405180830381865afa15801561403b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405f9190615e43565b6080015190508160020160009054906101000a90046001600160a01b03166001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156140ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140de9190615af1565b8015614103575081546001600160a01b0316600090815260028a016020526040902054155b156142755760028201546001600160a01b038581169116146142705781546040516370a0823160e01b81523060048201526000916141c5916001600160a01b039091169081906370a0823190602401602060405180830381865afa15801561416f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141939190615a0d565b8554604051631bf49af760e11b81526001600160a01b0391821660048201528c918e16906337e935ee906024016108fd565b83549091506142519082908a906141e4906001600160a01b0316615306565b8654604051633580ec1360e11b81526001600160a01b039182166004820152908e1690636b01d82690602401602060405180830381865afa15801561422d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097e9190615a0d565b61425b9087615a3c565b60028401549096506001600160a01b03169450505b614282565b61427f8186615a3c565b94505b50506000908152600188016020526040902054613f98565b50505b95945050505050565b6142ae6121a6565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586142e33390565b6040516001600160a01b03909116815260200160405180910390a1565b614308615374565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336142e3565b81546001600160a01b031680614382575081546001600160a01b039091166001600160a01b03199182168117835560009081526001909201602052604090912080549091169055565b600061438d83615306565b905060005b6001600160a01b0383161561107557836001600160a01b0316836001600160a01b031603156110755760006143c684615306565b905080831015614463576001600160a01b0382166143fc5785546001600160a01b0319166001600160a01b03861617865561442d565b6001600160a01b038281166000908152600188016020526040902080546001600160a01b0319169187169190911790555b506001600160a01b038481166000908152600187016020526040902080546001600160a01b031916918516919091179055611075565b600061446f87866152e3565b90506001600160a01b0381166144c35750506001600160a01b03808416600090815260018701602052604080822080549388166001600160a01b031994851681179091558252902080549091169055611075565b9391506143929050565b80546001600160a01b03166144df5750565b80546001600160a01b0316805b6001600160a01b03821615610c1e5760004261450784615306565b11612d25576040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015614555573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145799190615a0d565b90508015614654576000856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa1580156145c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e59190615b42565b50604051637e237e8960e01b8152306004820152602481018690529093506001600160a01b0384169250637e237e899150604401600060405180830381600087803b15801561463357600080fd5b505af1925050508015614644575060015b1561464e57600192505b50614659565b600191505b61466386866152e3565b925081156146e85785546001600160a01b039081169086160361469a5785546001600160a01b0319166001600160a01b0384161786555b6001600160a01b038085166000908152600188016020908152604080832080548589166001600160a01b03199182161790915593891683528083208054909416909355600289019052908120555b50509190506144ec565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a9082906370a0823190602401602060405180830381865afa158015614761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147859190615a0d565b6040518263ffffffff1660e01b81526004016147a391815260200190565b602060405180830381865afa1580156147c0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f229190615a0d565b82546000906001600160a01b03166147fe5750600061186b565b83546001600160a01b03165b6001600160a01b0381161561498957600061482482615306565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561486e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148929190615a0d565b604051633580ec1360e11b81526001600160a01b038581166004830152919250600091881690636b01d82690602401602060405180830381865afa1580156148de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149029190615a0d565b604051631bf49af760e11b81526001600160a01b03808716600483015291925060009161494091879186918b918d16906337e935ee906024016108fd565b9050428411156149675761495681888685612956565b6149609087615a3c565b9550614974565b6149718187615a3c565b95505b61497e89866152e3565b94505050505061480a565b509392505050565b8454600090819081906001600160a01b03166149b557506000915081905080614ae8565b87546001600160a01b03165b6001600160a01b03811615614ae6576040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015614a17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a3b9190615a0d565b90508015614ad457816001600160a01b0316886001600160a01b031603614a6d57614a668782615a3c565b9050600192505b604051631bf49af760e11b81526001600160a01b038084166004830152600091614aa991859185918b91908f16906337e935ee906024016108fd565b90506000614ab78483614db1565b9050614ac38188615a3c565b9650614acf8287615a3c565b955050505b614ade8a836152e3565b9150506149c1565b505b955095509592505050565b855460009081908190614b0e57506000915081905080614da5565b88546000905b8015614da257600081815260028c016020526040812080549091906001600160a01b03808c16911603614b4c57506001935087614d50565b60038201546040516372e3a5b160e01b8152600481018590526001600160a01b03909116906372e3a5b19060240160e060405180830381865afa158015614b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bbb9190615e43565b6080015190508160020160009054906101000a90046001600160a01b03166001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c3a9190615af1565b8015614c5f575081546001600160a01b0316600090815260028d016020526040902054155b15614d505760028201546001600160a01b03858116911614614d505781546040516370a0823160e01b8152306004820152614d3d916001600160a01b03169081906370a0823190602401602060405180830381865afa158015614cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cea9190615a0d565b8a8e6001600160a01b03166337e935ee8760000160009054906101000a90046001600160a01b03166040518263ffffffff1660e01b81526004016108fd91906001600160a01b0391909116815260200190565b60028301546001600160a01b0316945090505b8015614d8a578154600090614d6e906001600160a01b031683614db1565b9050614d7a8189615a3c565b9750614d868288615a3c565b9650505b5050600090815260018b016020526040902054614b14565b50505b96509650969350505050565b600080614dbd84615306565b905042811115614de2576000614dd2826153bd565b9050614dde8482615a62565b9250505b5092915050565b6000614e3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153c99092919063ffffffff16565b9050805160001480614e5f575080806020019051810190614e5f9190615af1565b610c1e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610741565b614ec88282611889565b610c9d57614ed5816153d8565b614ee08360206153ea565b604051602001614ef1929190615edf565b60408051601f198184030181529082905262461bcd60e51b8252610741916004016159c5565b801580614f915750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614f6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f8f9190615a0d565b155b614ffc5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610741565b6040516001600160a01b038316602482015260448101829052610c1e90849063095ea7b360e01b90606401612cee565b60008360000154905081604001516001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015615077573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061509b9190615af1565b156150a557600080fd5b80615125575081835560009182526001808401602090815260408085208590556002958601825293849020835181546001600160a01b03199081166001600160a01b039283161783559285015193820193909355938301519484018054821695831695909517909455606090910151600390920180549093169116179055565b60005b81156151e1578184146151e157600082815260028087016020526040918290200154908401516001600160a01b0391821691811682111561519e578261517057858755615184565b600083815260018801602052604090208690555b5050600084815260018601602052604090208290556151e1565b6000848152600188016020526040902054806151d65750505060008281526001860160205260408082208690558582528120556151e1565b939250615128915050565b5050600091825260029283016020908152604092839020825181546001600160a01b039182166001600160a01b03199182161783559284015160018301559383015194810180549585169583169590951790945560609091015160039093018054939092169216919091179055565b805460009061526157506000919050565b81545b80156137a7578161527481615e2a565b6000928352600185016020526040909220549192506152649050565b80546000906001600160a01b03166152aa57506000919050565b81546001600160a01b03165b6001600160a01b038116156137a757816152cf81615e2a565b9250506152dc83826152e3565b90506152b6565b6001600160a01b0380821660009081526001840160205260409020541692915050565b6000816001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa158015615346573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061536a9190615b42565b5091949350505050565b60005460ff16611b005760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610741565b6000610ad84283615a4f565b60606129de8484600085615586565b6060610ad86001600160a01b03831660145b606060006153f9836002615a62565b615404906002615a3c565b67ffffffffffffffff81111561541c5761541c615cad565b6040519080825280601f01601f191660200182016040528015615446576020820181803683370190505b509050600360fc1b8160008151811061546157615461615cc3565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061549057615490615cc3565b60200101906001600160f81b031916908160001a90535060006154b4846002615a62565b6154bf906001615a3c565b90505b6001811115615537576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106154f3576154f3615cc3565b1a60f81b82828151811061550957615509615cc3565b60200101906001600160f81b031916908160001a90535060049490941c9361553081615f54565b90506154c2565b50831561186b5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610741565b6060824710156155e75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610741565b600080866001600160a01b031685876040516156039190615f6b565b60006040518083038185875af1925050503d8060008114615640576040519150601f19603f3d011682016040523d82523d6000602084013e615645565b606091505b509150915061294b87838387606083156156c05782516000036156b9576001600160a01b0385163b6156b95760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610741565b50816129de565b6129de83838151156156d55781518083602001fd5b8060405162461bcd60e51b815260040161074191906159c5565b6001600160a01b0381168114610cb257600080fd5b6000806040838503121561571757600080fd5b8235615722816156ef565b946020939093013593505050565b60006020828403121561574257600080fd5b81356001600160e01b03198116811461186b57600080fd5b60006020828403121561576c57600080fd5b813561186b816156ef565b8015158114610cb257600080fd5b6000806040838503121561579857600080fd5b82356157a3816156ef565b915060208301356157b381615777565b809150509250929050565b6000602082840312156157d057600080fd5b5035919050565b600080604083850312156157ea57600080fd5b8235915060208301356157b3816156ef565b600080600080600060a0868803121561581457600080fd5b853561581f816156ef565b9450602086013561582f816156ef565b94979496505050506040830135926060810135926080909101359150565b6020808252825182820181905260009190848201906040850190845b8181101561588557835183529284019291840191600101615869565b50909695505050505050565b60005b838110156158ac578181015183820152602001615894565b50506000910152565b600081518084526158cd816020860160208601615891565b601f01601f19169290920160200192915050565b82151581526040602082015260006129de60408301846158b5565b60008060006040848603121561591157600080fd5b833561591c816156ef565b9250602084013567ffffffffffffffff8082111561593957600080fd5b818601915086601f83011261594d57600080fd5b81358181111561595c57600080fd5b8760208260051b850101111561597157600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b818110156158855783516001600160a01b0316835292840192918401916001016159a0565b60208152600061186b60208301846158b5565b6000806000606084860312156159ed57600080fd5b83356159f8816156ef565b95602085013595506040909401359392505050565b600060208284031215615a1f57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610ad857610ad8615a26565b81810381811115610ad857610ad8615a26565b8082028115828204841417610ad857610ad8615a26565b600082615a9657634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615aad57600080fd5b815161186b816156ef565b6020808252810182905260006001600160fb1b03831115615ad857600080fd5b8260051b80856040850137919091016040019392505050565b600060208284031215615b0357600080fd5b815161186b81615777565b600181811c90821680615b2257607f821691505b6020821081036137a757634e487b7160e01b600052602260045260246000fd5b60008060008060808587031215615b5857600080fd5b845193506020850151615b6a816156ef565b6040860151909350615b7b816156ef565b6060860151909250615b8c816156ef565b939692955090935050565b600060208284031215615ba957600080fd5b815160ff8116811461186b57600080fd5b600181815b80851115615bf5578160001904821115615bdb57615bdb615a26565b80851615615be857918102915b93841c9390800290615bbf565b509250929050565b600082615c0c57506001610ad8565b81615c1957506000610ad8565b8160018114615c2f5760028114615c3957615c55565b6001915050610ad8565b60ff841115615c4a57615c4a615a26565b50506001821b610ad8565b5060208310610133831016604e8410600b8410161715615c78575081810a610ad8565b615c828383615bba565b8060001904821115615c9657615c96615a26565b029392505050565b600061186b60ff841683615bfd565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b602080825282518282018190526000919060409081850190868401855b82811015615d4657815180518552868101516001600160a01b0390811688870152868201518787015260608083015190870152608091820151169085015260a09093019290850190600101615cf6565b5091979650505050505050565b604051601f8201601f1916810167ffffffffffffffff81118282101715615d7c57615d7c615cad565b604052919050565b60006020808385031215615d9757600080fd5b825167ffffffffffffffff80821115615daf57600080fd5b818501915085601f830112615dc357600080fd5b815181811115615dd557615dd5615cad565b8060051b9150615de6848301615d53565b8181529183018401918481019088841115615e0057600080fd5b938501935b83851015615e1e57845182529385019390850190615e05565b98975050505050505050565b600060018201615e3c57615e3c615a26565b5060010190565b600060e08284031215615e5557600080fd5b60405160e0810181811067ffffffffffffffff82111715615e7857615e78615cad565b604052825181526020830151615e8d816156ef565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615ec0816156ef565b60a082015260c0830151615ed381615777565b60c08201529392505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615f17816017850160208801615891565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615f48816028840160208801615891565b01602801949350505050565b600081615f6357615f63615a26565b506000190190565b60008251615f7d818460208701615891565b919091019291505056fe7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55a26469706673582212204858979e1cc3d5ca7b21190dda166390ee2621246bdd11b671082dfeaed851a064736f6c63430008170033000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e60000000000000000000000003c6b0398eed7dafcb3c13d482400329a6e25acd200000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d960000000000000000000000003210cf0892b794a9403f958fdb94ae2e13ac451f00000000000000000000000021fc7b250ccaeecdb2abb38e04617d1f24d9877200000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000000000000003b538000000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001553686f7265776f6f6473205072696d6520555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000001674737653686f7265776f6f64735072696d655553444300000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102945760003560e01c806372db6dfc11610167578063aeaaa299116100ce578063e3bafad211610087578063e3bafad2146106ad578063e58bb639146106c0578063ed22088e146106c8578063f235757f146106db578063fb06a3bd146106ee578063fde813a8146105f057610294565b8063aeaaa29914610631578063aff518f314610639578063b7bd869c1461064c578063d19a3bb81461065f578063d547741f14610692578063d8c143f7146106a557610294565b806391d148541161012057806391d14854146105c857806395d89b41146105db5780639d7fb70c146105f0578063a217fddf14610603578063ad5681591461060b578063add5ba4b1461061e57610294565b806372db6dfc1461054f578063740152831461056257806383076af9146105905780638c02ea3a146105a55780638f90ae5a146105b8578063903c2c06146105c057610294565b806346aa2f121161020b5780635c975abb116101c45780635c975abb146104e35780635d265d3f146104ee57806367747e571461052457806369026e88146105375780636bc912e01461053f578063702848131461054757610294565b806346aa2f121461047557806349317f1d1461048a578063503160d9146104925780635134f576146104a55780635157ced5146104b857806354c885e0146104c057610294565b8063176eacd31161025d578063176eacd3146103e5578063248a9ca3146103f85780632f2ff15d1461041c57806336568abe1461042f5780633d6cb57514610442578063442e61aa1461045557610294565b80625446f2146102cf57806301ffc9a7146102e257806304bd46291461030a57806311b4e2531461032b57806312345134146103d2575b73bb51273d6c746910c7c06fe718f30c936170fed03660008037600080366000845af43d6000803e8080156102c8573d6000f35b3d6000fd5b005b6102cd6102dd366004615704565b6106f6565b6102f56102f0366004615730565b610aa7565b60405190151581526020015b60405180910390f35b61031d61031836600461575a565b610ade565b604051908152602001610301565b600b54600c54600d54600e54600f5460105460115460125460135460145461036d996001600160a01b039081169981169881169781169681169516939291908a565b604080516001600160a01b039b8c168152998b1660208b0152978a169789019790975294881660608801529287166080870152951660a085015260c084019490945260e083019390935261010082019290925261012081019190915261014001610301565b6102cd6103e0366004615785565b610ae8565b61031d6103f336600461575a565b610baf565b61031d6104063660046157be565b6000908152600160208190526040909120015490565b6102cd61042a3660046157d7565b610bf8565b6102cd61043d3660046157d7565b610c23565b6102cd6104503660046157be565b610ca1565b6104686104633660046157fc565b610cb5565b604051610301919061584d565b61031d61048336600461575a565b5060001990565b61031d610f10565b6102cd6104a03660046157be565b610f27565b6102cd6104b33660046157be565b610f38565b6102cd610fde565b6102f56104ce36600461575a565b60156020526000908152604090205460ff1681565b60005460ff166102f5565b60408051600481526024810182526020810180516001600160e01b031663440368a360e01b1790529051610301916000916158e1565b6102cd6105323660046157be565b61107c565b6102cd611122565b6104686111a8565b61031d6111b4565b6102cd61055d3660046158fc565b6111c6565b610575610570366004615704565b611380565b60408051938452602084019290925290820152606001610301565b61059861160a565b6040516103019190615984565b61031d6105b336600461575a565b611616565b61031d611872565b61031d61187f565b6102f56105d63660046157d7565b611889565b6105e36118b4565b60405161030191906159c5565b6102cd6105fe3660046157be565b611946565b61031d600081565b61031d6106193660046159d8565b61194e565b6102cd61062c3660046157be565b611a50565b6102cd611af6565b6102cd61064736600461575a565b611b02565b6102cd61065a36600461575a565b611cd7565b61067a73bb51273d6c746910c7c06fe718f30c936170fed081565b6040516001600160a01b039091168152602001610301565b6102cd6106a03660046157d7565b611e21565b6102cd611e47565b6102cd6106bb366004615704565b611e77565b6102cd611f2f565b6102cd6106d63660046157be565b61207f565b6102cd6106e936600461575a565b612125565b6102cd612179565b6106fe6121a6565b6001600160a01b038216600090815260156020526040902054829060ff161561074a57604051633d764d2b60e11b81526001600160a01b03821660048201526024015b60405180910390fd5b6000821161075757600080fd5b610760836121ec565b6107885760405163017dad6760e71b81526001600160a01b0384166004820152602401610741565b60105460009081906107c89060039087906001600160a01b03167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861231b565b91509150816107f55760405163017dad6760e71b81526001600160a01b0386166004820152602401610741565b6107ff6000612532565b60006108096126b2565b90506000811161081857600080fd5b60006108238261275a565b90506000811161083257600080fd5b601054604051633580ec1360e11b81526001600160a01b0389811660048301526000921690636b01d82690602401602060405180830381865afa15801561087d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190615a0d565b601054604051631bf49af760e11b81526001600160a01b03808c166004830152929350600092610943928c928c927f00000000000000000000000000000000000000000000000000000000000f42409216906337e935ee906024015b602060405180830381865afa15801561091a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093e9190615a0d565b6127e7565b90506000610983827f00000000000000000000000000000000000000000000000000000000000f424088600b600801548761097e9190615a3c565b612956565b9050808510156109b057604051633bf1b8e760e11b81526004810186905260248101829052604401610741565b60006109c68b8b6109c1858a615a4f565b6129e6565b6011549091508111156109ec57604051632391f20360e21b815260040160405180910390fd5b6000856109f98489615a4f565b610a0b90670de0b6b3a7640000615a62565b610a159190615a79565b601254909150811015610a3b57604051631342c8d960e11b815260040160405180910390fd5b610a478c858886612be3565b610a5083612c22565b610a656001600160a01b038d1633308e612cba565b610a996001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48163385612d2b565b505050505050505050505050565b60006001600160e01b03198216637965db0b60e01b1480610ad857506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000610ad86126b2565b600080516020615f88833981519152610b0081612d5b565b604051637948ecd560e01b81526001600160a01b03848116600483015283151560248301527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d961690637948ecd590604401600060405180830381600087803b158015610b6b57600080fd5b505af1158015610b7f573d6000803e3d6000fd5b505050506001600160a01b03929092166000908152601560205260409020805460ff191691151591909117905550565b60006001600160a01b038216610bdb5760405163017dad6760e71b815260006004820152602401610741565b610ad8826000610bf1610bec6126b2565b61275a565b6000612d65565b60008281526001602081905260409091200154610c1481612d5b565b610c1e8383612e3b565b505050565b6001600160a01b0381163314610c935760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610741565b610c9d8282612ea6565b5050565b610ca9612f0d565b610cb281612f44565b50565b6060610cbf6121a6565b6001600160a01b038516600090815260156020526040902054859060ff1615610d0657604051633d764d2b60e11b81526001600160a01b0382166004820152602401610741565b6040516348e4a64960e01b81523360048201527f0000000000000000000000004b10deba312d6ff12fda13535009816660fc44636001600160a01b0316906348e4a6499060240160006040518083038186803b158015610d6557600080fd5b505afa158015610d79573d6000803e3d6000fd5b5050505082600003610d9e5760405163b97ce22960e01b815260040160405180910390fd5b6000610daa8888612f55565b9050610db66000612532565b6000868152600960205260409020600101548490610dfb6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b88815230602082015260408101889052606081018790527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03166080820152610e4f8b85838d8787613255565b95506000610e5b6126b2565b90506000610e688261275a565b905060008111610e7757600080fd5b600081610e8c84670de0b6b3a7640000615a62565b610e969190615a79565b601254909150811015610ebc57604051631342c8d960e11b815260040160405180910390fd5b6000610eca600080866129e6565b601154909150811115610ef057604051632391f20360e21b815260040160405180910390fd5b610efe8e6000856000612be3565b50505050505050505095945050505050565b6000610f1a612f0d565b610f2261369c565b905090565b610f2f612f0d565b610cb2816136bb565b600080516020615f88833981519152610f5081612d5b565b601454604051632efd6ecd60e21b81526004810191909152602481018390527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d966001600160a01b03169063bbf5bb3490604401600060405180830381600087803b158015610fbd57600080fd5b505af1158015610fd1573d6000803e3d6000fd5b5050506014929092555050565b600080516020615f88833981519152610ff681612d5b565b6002805460ff19169055604080516305ad0e1f60e51b815290516001600160a01b037f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d96169163b5a1c3e091600480830192600092919082900301818387803b15801561106157600080fd5b505af1158015611075573d6000803e3d6000fd5b5050505050565b600080516020615f8883398151915261109481612d5b565b60125460405163627d8a0160e11b81526004810191909152602481018390527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d966001600160a01b03169063c4fb140290604401600060405180830381600087803b15801561110157600080fd5b505af1158015611115573d6000803e3d6000fd5b5050506012929092555050565b600080516020615f8883398151915261113a81612d5b565b6002805460ff1916600117905560408051631022cfd760e01b815290516001600160a01b037f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d961691631022cfd791600480830192600092919082900301818387803b15801561106157600080fd5b6060610f2260076136f1565b6000610f226111c16126b2565b6137ad565b6040516348e4a64960e01b81523360048201527f0000000000000000000000004b10deba312d6ff12fda13535009816660fc44636001600160a01b0316906348e4a6499060240160006040518083038186803b15801561122557600080fd5b505afa158015611239573d6000803e3d6000fd5b50505050611246836121ec565b61126e5760405163f14a3cf960e01b81526001600160a01b0384166004820152602401610741565b60008390506000816001600160a01b031663505d799e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d79190615a9b565b60405163720e52c560e01b81529091506001600160a01b0382169063720e52c5906113089087908790600401615ab8565b600060405180830381600087803b15801561132257600080fd5b505af1158015611336573d6000803e3d6000fd5b50506010546113769250600791506003906001600160a01b03167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486137e9565b6110756000612532565b60008060008061138e6126b2565b90506000806001600160a01b03881615611580576113ab886121ec565b6113d35760405163017dad6760e71b81526001600160a01b0389166004820152602401610741565b60008061140260038b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48613c65565b915091508161142f5760405163017dad6760e71b81526001600160a01b038b166004820152602401610741565b601054604051633580ec1360e11b81526001600160a01b038c811660048301526000921690636b01d82690602401602060405180830381865afa15801561147a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149e9190615a0d565b601054604051631bf49af760e11b81526001600160a01b038e81166004830152929350600092909116906337e935ee90602401602060405180830381865afa1580156114ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115129190615a0d565b90506115408c8c7f00000000000000000000000000000000000000000000000000000000000f4240846127e7565b9550611579867f00000000000000000000000000000000000000000000000000000000000f424085600b600801548661097e9190615a3c565b9450505050505b61158f88886109c18487615a4f565b95506001600160a01b038816156115b7576115b488836115ae8661275a565b84612d65565b94505b60006115c28461275a565b9050806000036115d557600094506115ff565b806115e08386615a4f565b6115f290670de0b6b3a7640000615a62565b6115fc9190615a79565b94505b505050509250925092565b6060610f226003613eb9565b6001600160a01b03811660009081526005602052604081205481901561182457600f5460405163e7e4b8db60e01b81526001600160a01b038581166004830152600092169063e7e4b8db90602401602060405180830381865afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a59190615af1565b156116bc5750600f546001600160a01b031661173d565b600e5460405163e7e4b8db60e01b81526001600160a01b0386811660048301529091169063e7e4b8db90602401602060405180830381865afa158015611706573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172a9190615af1565b1561173d5750600e546001600160a01b03165b60105460405163739d020f60e11b81526001600160a01b038381166004830152808716602483015261182092879291169063e73a041e90604401602060405180830381865afa158015611794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b89190615a0d565b6040516370a0823160e01b81523060048201526001600160a01b038816906370a0823190602401602060405180830381865afa1580156117fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106199190615a0d565b9150505b601054611861906007906003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000f424087613f81565b61186b9082615a3c565b9392505050565b6000610f22610bec6126b2565b6000610f226126b2565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060600a80546118c390615b0e565b80601f01602080910402602001604051908101604052809291908181526020018280546118ef90615b0e565b801561193c5780601f106119115761010080835404028352916020019161193c565b820191906000526020600020905b81548152906001019060200180831161191f57829003601f168201915b5050505050905090565b610cb2612f0d565b600080846001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190615b42565b5050601054604051631bf49af760e11b81526001600160a01b03808a166004830152939450600093611a189350899288927f00000000000000000000000000000000000000000000000000000000000f424092909116906337e935ee906024016108fd565b9050611a46817f00000000000000000000000000000000000000000000000000000000000f42408488612956565b9695505050505050565b600080516020615f88833981519152611a6881612d5b565b6013546040516309209da160e01b81526004810191909152602481018390527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d966001600160a01b0316906309209da190604401600060405180830381600087803b158015611ad557600080fd5b505af1158015611ae9573d6000803e3d6000fd5b5050506013929092555050565b611b006000612532565b565b600080516020615f88833981519152611b1a81612d5b565b6001600160a01b038216611b2d57600080fd5b60006001600160a01b0316826001600160a01b031663025f1a5e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9a9190615a9b565b6001600160a01b031603611bad57600080fd5b6003546001600160a01b03165b6001600160a01b03811615611c1a57611bd2816121ec565b611bfa5760405163017dad6760e71b81526001600160a01b0382166004820152602401610741565b6001600160a01b0390811660009081526004602052604090205416611bba565b600f546040516333d864dd60e21b81526001600160a01b03918216600482018190528583166024830152917f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d96169063cf61937490604401600060405180830381600087803b158015611c8b57600080fd5b505af1158015611c9f573d6000803e3d6000fd5b5050600e80546001600160a01b039485166001600160a01b031991821617909155600f80549790941696169590951790915550505050565b600080516020615f88833981519152611cef81612d5b565b600082905060006001600160a01b0316816001600160a01b03166342a9d1316040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d619190615a9b565b6001600160a01b031603611d7457600080fd5b6010546040516399146dff60e01b81526001600160a01b03918216600482015284821660248201527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d96909116906399146dff90604401600060405180830381600087803b158015611de457600080fd5b505af1158015611df8573d6000803e3d6000fd5b5050601080546001600160a01b0319166001600160a01b03949094169390931790925550505050565b60008281526001602081905260409091200154611e3d81612d5b565b610c1e8383612ea6565b600080516020615f88833981519152611e5f81612d5b565b611e676142a6565b506002805460ff19166001179055565b600080516020615f88833981519152611e8f81612d5b565b604051633759d6ad60e11b81526001600160a01b038481166004830152602482018490527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d961690636eb3ad5a90604401600060405180830381600087803b158015611ef957600080fd5b505af1158015611f0d573d6000803e3d6000fd5b505050506001600160a01b039290921660009081526006602052604090205550565b60025461010090046001600160a01b03163314611f815760405162461bcd60e51b815260206004820152601060248201526f10b832b73234b733a3b7bb32b93737b960811b6044820152606401610741565b600d54611fa690600080516020615f88833981519152906001600160a01b0316612ea6565b600254611fd090600080516020615f888339815191529061010090046001600160a01b0316612e3b565b600254600d80546101009092046001600160a01b039081166001600160a01b0319909316831790915560405163a12f83a960e01b815260048101929092527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d96169063a12f83a990602401600060405180830381600087803b15801561205457600080fd5b505af1158015612068573d6000803e3d6000fd5b505060028054610100600160a81b03191690555050565b600080516020615f8883398151915261209781612d5b565b6011546040516368e5255760e01b81526004810191909152602481018390527f00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d966001600160a01b0316906368e5255790604401600060405180830381600087803b15801561210457600080fd5b505af1158015612118573d6000803e3d6000fd5b5050506011929092555050565b600080516020615f8883398151915261213d81612d5b565b6001600160a01b03821661215057600080fd5b50600280546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b600080516020615f8883398151915261219181612d5b565b612199614300565b506002805460ff19169055565b60005460ff1615611b005760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610741565b600f54600e546000916001600160a01b0390811691168115801590612276575060405163e7e4b8db60e01b81526001600160a01b03858116600483015283169063e7e4b8db90602401602060405180830381865afa158015612252573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122769190615af1565b15612285575060019392505050565b6001600160a01b03811615801590612302575060405163e7e4b8db60e01b81526001600160a01b03858116600483015282169063e7e4b8db90602401602060405180830381865afa1580156122de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123029190615af1565b15612311575060019392505050565b5060009392505050565b6001600160a01b03831660009081526002850160205260408120548190801561245757856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa15801561237c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a09190615b42565b509193505050428210156123b8576000925050612529565b604051633580ec1360e11b81526001600160a01b03878116600483015260009190871690636b01d82690602401602060405180830381865afa92505050801561241e575060408051601f3d908101601f1916820190925261241b91810190615a0d565b60015b156124265790505b801561245157808214612451576001600160a01b038716600090815260028901602052604090208190555b50612523565b604051633580ec1360e11b81526001600160a01b038781166004830152861690636b01d82690602401602060405180830381865afa9250505080156124b9575060408051601f3d908101601f191682019092526124b691810190615a0d565b60015b6124c7575060009150612529565b80156124d357806124d6565b60015b91505060006124e6888887613c65565b93509050806124fa57600093505050612529565b6125048888614339565b506001600160a01b038616600090815260028801602052604090208190555b60019250505b94509492505050565b60105461256e906007906003906001600160a01b03167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486137e9565b61257860036144cd565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a0823190602401602060405180830381865afa1580156125df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126039190615a0d565b90508181111561269f57604051636e553f6560e01b815282820360048201523060248201527f00000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e66001600160a01b031690636e553f65906044016020604051808303816000875af115801561267b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1e9190615a0d565b81811015610c9d57610c9d818303612c22565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa15801561271b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273f9190615a0d565b90508061274a6146f2565b6127549190615a3c565b91505090565b60105460009061279a906007906003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000f424085613f81565b6010546127d3906003906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000f42406147e4565b6127dd9084615a3c565b610ad89190615a3c565b600080856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612828573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284c9190615b97565b61285790600a615c9e565b90506000866001600160a01b031663ef4474cd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612899573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128bd9190615a0d565b9050836000036128fd576128d9670de0b6b3a764000083615a62565b856128e48884615a62565b6128ee9190615a62565b6128f89190615a79565b61294b565b61290f670de0b6b3a764000083615a62565b61292190670de0b6b3a7640000615a62565b858761292d8785615a62565b6129379190615a62565b6129419190615a62565b61294b9190615a79565b979650505050505050565b600080834211612989576301da9c00856129704287615a4f565b61297a9190615a62565b6129849190615a79565b61298c565b60005b9050670de0b6b3a76400006129a18285615a62565b6129ab9190615a79565b6129b59086615a3c565b6129bf8688615a62565b6129c99190615a79565b91508582116129d857816129da565b855b9150505b949350505050565b60105460009081908190819081908190612a2e906003906001600160a01b03168b8b7f00000000000000000000000000000000000000000000000000000000000f4240614991565b91945092509050612a3f8386615a3c565b9450612a4b8285615a3c565b60105490945060009081908190612a93906007906003906001600160a01b03168f8f7f00000000000000000000000000000000000000000000000000000000000f4240614af3565b91945092509050612aa48389615a3c565b9750612ab08288615a3c565b965083158015612abe575080155b8015612ad257506001600160a01b038c1615155b15612b9d57601054604051631bf49af760e11b81526001600160a01b038e8116600483015260009216906337e935ee90602401602060405180830381865afa158015612b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b469190615a0d565b90506000612b768e8e7f00000000000000000000000000000000000000000000000000000000000f4240856127e7565b9050612b82818a615a3c565b9850612b8e8e82614db1565b612b98908b615a3c565b995050505b86158015612ba9575089155b15612bbf5760009850505050505050505061186b565b612bc98a88615a3c565b612bd39089615a79565b9c9b505050505050505050505050565b6000612bf185858585612d65565b60145490915081111561107557604051633cb36e3560e11b81526001600160a01b0386166004820152602401610741565b604051632d182be560e21b815260048101829052306024820181905260448201527f00000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e66001600160a01b03169063b460af94906064016020604051808303816000875af1158015612c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9d9190615a0d565b6040516001600160a01b0380851660248301528316604482015260648101829052612d259085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614de9565b50505050565b6040516001600160a01b038316602482015260448101829052610c1e90849063a9059cbb60e01b90606401612cee565b610cb28133614ebe565b60008084612d7287611616565b612d7c9190615a3c565b9050600083612d8b8787615a3c565b612d959190615a4f565b90507f00000000000000000000000000000000000000000000000000000000000f4240612dca83670de0b6b3a7640000615a62565b612dd49190615a79565b91507f00000000000000000000000000000000000000000000000000000000000f4240612e0982670de0b6b3a7640000615a62565b612e139190615a79565b90508015612e2e57806128ee83670de0b6b3a7640000615a62565b5060009695505050505050565b612e458282611889565b610c9d5760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b612eb08282611889565b15610c9d5760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b333014611b005760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606401610741565b612f4c6121a6565b610cb281612532565b6000612f60836121ec565b612f885760405163f14a3cf960e01b81526001600160a01b0384166004820152602401610741565b612f91826121ec565b612fb95760405163017dad6760e71b81526001600160a01b0383166004820152602401610741565b816001600160a01b031663cc5b6e4a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ff7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061301b9190615a0d565b836001600160a01b031663cc5b6e4a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307d9190615a0d565b146130a65760405163017dad6760e71b81526001600160a01b0383166004820152602401610741565b60006130d46003847f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48613c65565b509050806131005760405163017dad6760e71b81526001600160a01b0384166004820152602401610741565b6000846001600160a01b031663505d799e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613140573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131649190615a9b565b9050806001600160a01b031663eb54f9ec6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131c89190615a0d565b421115806132375750806001600160a01b031663ba829d716040518163ffffffff1660e01b8152600401602060405180830381865afa15801561320f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132339190615a0d565b4210155b156129de5760405163f046007760e01b815260040160405180910390fd5b60606000866001600160a01b031663357a77d26040518163ffffffff1660e01b8152600401602060405180830381865afa158015613297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bb9190615a9b565b60408051600180825281830190925291925060009190816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816132d6579050509050868160008151811061332757613327615cc3565b6020026020010181905250838511156134165783850360006133476126b2565b90508181101561337457604051633bf1b8e760e11b81526004810182905260248101839052604401610741565b61337d82612c22565b613413846001600160a01b0316635c4b440e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133e29190615a9b565b6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169084614f17565b50505b60405163db875b6960e01b81526001600160a01b0389169063db875b6990613442908490600401615cd9565b6000604051808303816000875af1158015613461573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134899190810190615d84565b925082516000036134ad57604051636df5846d60e01b815260040160405180910390fd5b8360000361352957613524836000815181106134cb576134cb615cc3565b60200260200101516040518060800160405280896001600160a01b031681526020018a6060015181526020018c6001600160a01b031681526020018b6001600160a01b0316815250600761502c9092919063ffffffff16565b61356c565b6000600760020160008560008151811061354557613545615cc3565b60200260200101518152602001908152602001600020905087606001518160010181905550505b83851015613690576040516370a0823160e01b81523060048201526001600160a01b037f00000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e6811691636e553f65917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa158015613602573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136269190615a0d565b6040516001600160e01b031960e084901b16815260048101919091523060248201526044016020604051808303816000875af115801561366a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061368e9190615a0d565b505b50509695505050505050565b60006136a66121a6565b6136b06000612532565b610f22610bec6126b2565b6136c36121a6565b60025460ff16156136e75760405163035edea360e41b815260040160405180910390fd5b610cb26000612532565b606060006136fe83615250565b905080156137a7578067ffffffffffffffff81111561371f5761371f615cad565b604051908082528060200260200182016040528015613748578160200160208202803683370190505b5083549092506000905b80156137a45780848361376481615e2a565b94508151811061377657613776615cc3565b60200260200101818152505061379d85826000908152600191909101602052604090205490565b9050613752565b50505b50919050565b6000806137b98361275a565b9050806000036137cc5750600092915050565b806137df84670de0b6b3a7640000615a62565b61186b9190615a79565b835415612d25578354805b8115613c5d576000828152600287810160209081526040808420815160808101835281546001600160a01b0390811682526001808401548387015295830154811682850152600390920154821660608201908152888752948c0190935281852054935191516372e3a5b160e01b8152600481018890529294929116906372e3a5b19060240160e060405180830381865afa158015613896573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ba9190615e43565b608001519050600083604001516001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613904573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139289190615af1565b15613aa9575082516001906000908190613945908c908c8c61231b565b915091508115801561395657508042115b15613aa2576000866000015190506000816001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa1580156139a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c89190615b42565b506040516370a0823160e01b815230600482018190529194508493506001600160a01b038085169350637e237e899291908716906370a0823190602401602060405180830381865afa158015613a22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a469190615a0d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015613a8c57600080fd5b505af1925050508015613a9d575060015b505050505b5050613bd5565b83604001516001600160a01b0316638dfea9e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015613aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0f9190615af1565b15613bc957604080516001808252818301909252600091602080830190803683370190505090508681600081518110613b4a57613b4a615cc3565b60200260200101818152505084606001516001600160a01b031663720e52c5826040518263ffffffff1660e01b8152600401613b86919061584d565b600060405180830381600087803b158015613ba057600080fd5b505af1925050508015613bb1575060015b613bbe5760009150613bc3565b600191505b50613bd5565b81600003613bd5575060015b8015613c505760008681526001808c01602090815260408084208490556002808f01909252832080546001600160a01b0319908116825592810193909355820180548216905560039091018054909116905589548603613c3757828a55613c50565b600085815260018b016020526040902083905593945084935b50909392506137f4915050565b505050505050565b600080600080856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa158015613ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ccd9190615b42565b929550909350909150506001600160a01b0380831690861614613cf557600093505050613eb1565b42831015613d0857600093505050613eb1565b6000816001600160a01b031663190e80126040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d6c9190615b97565b60ff16905060005b81811015613ea85760405163172c48c760e01b8152600481018290526000906001600160a01b0385169063172c48c790602401602060405180830381865afa158015613dc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613de89190615a9b565b6001600160a01b038116600090815260038c016020526040812054919250819003613e1c5760009750505050505050613eb1565b6040516320db25d560e01b81526001600160a01b0383811660048301528291908716906320db25d590602401602060405180830381865afa158015613e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e899190615a0d565b1015613e9e5760009750505050505050613eb1565b5050600101613d74565b50600194505050505b935093915050565b60606000613ec683615290565b905080156137a7578067ffffffffffffffff811115613ee757613ee7615cad565b604051908082528060200260200182016040528015613f10578160200160208202803683370190505b5083549092506000906001600160a01b03165b6001600160a01b038116156137a457808483613f3e81615e2a565b945081518110613f5057613f50615cc3565b60200260200101906001600160a01b031690816001600160a01b031681525050613f7a85826152e3565b9050613f23565b8454600090613f925750600061429d565b85546000905b801561429a57600081815260028901602052604090206001600160a01b03851615801590613fd3575080546001600160a01b03868116911614155b15613fef57506000908152600188016020526040902054613f98565b60038101546040516372e3a5b160e01b8152600481018490526000916001600160a01b0316906372e3a5b19060240160e060405180830381865afa15801561403b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405f9190615e43565b6080015190508160020160009054906101000a90046001600160a01b03166001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156140ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140de9190615af1565b8015614103575081546001600160a01b0316600090815260028a016020526040902054155b156142755760028201546001600160a01b038581169116146142705781546040516370a0823160e01b81523060048201526000916141c5916001600160a01b039091169081906370a0823190602401602060405180830381865afa15801561416f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141939190615a0d565b8554604051631bf49af760e11b81526001600160a01b0391821660048201528c918e16906337e935ee906024016108fd565b83549091506142519082908a906141e4906001600160a01b0316615306565b8654604051633580ec1360e11b81526001600160a01b039182166004820152908e1690636b01d82690602401602060405180830381865afa15801561422d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097e9190615a0d565b61425b9087615a3c565b60028401549096506001600160a01b03169450505b614282565b61427f8186615a3c565b94505b50506000908152600188016020526040902054613f98565b50505b95945050505050565b6142ae6121a6565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586142e33390565b6040516001600160a01b03909116815260200160405180910390a1565b614308615374565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336142e3565b81546001600160a01b031680614382575081546001600160a01b039091166001600160a01b03199182168117835560009081526001909201602052604090912080549091169055565b600061438d83615306565b905060005b6001600160a01b0383161561107557836001600160a01b0316836001600160a01b031603156110755760006143c684615306565b905080831015614463576001600160a01b0382166143fc5785546001600160a01b0319166001600160a01b03861617865561442d565b6001600160a01b038281166000908152600188016020526040902080546001600160a01b0319169187169190911790555b506001600160a01b038481166000908152600187016020526040902080546001600160a01b031916918516919091179055611075565b600061446f87866152e3565b90506001600160a01b0381166144c35750506001600160a01b03808416600090815260018701602052604080822080549388166001600160a01b031994851681179091558252902080549091169055611075565b9391506143929050565b80546001600160a01b03166144df5750565b80546001600160a01b0316805b6001600160a01b03821615610c1e5760004261450784615306565b11612d25576040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015614555573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145799190615a0d565b90508015614654576000856001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa1580156145c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e59190615b42565b50604051637e237e8960e01b8152306004820152602481018690529093506001600160a01b0384169250637e237e899150604401600060405180830381600087803b15801561463357600080fd5b505af1925050508015614644575060015b1561464e57600192505b50614659565b600191505b61466386866152e3565b925081156146e85785546001600160a01b039081169086160361469a5785546001600160a01b0319166001600160a01b0384161786555b6001600160a01b038085166000908152600188016020908152604080832080548589166001600160a01b03199182161790915593891683528083208054909416909355600289019052908120555b50509190506144ec565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e66001600160a01b0316906307a2d13a9082906370a0823190602401602060405180830381865afa158015614761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147859190615a0d565b6040518263ffffffff1660e01b81526004016147a391815260200190565b602060405180830381865afa1580156147c0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f229190615a0d565b82546000906001600160a01b03166147fe5750600061186b565b83546001600160a01b03165b6001600160a01b0381161561498957600061482482615306565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561486e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148929190615a0d565b604051633580ec1360e11b81526001600160a01b038581166004830152919250600091881690636b01d82690602401602060405180830381865afa1580156148de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149029190615a0d565b604051631bf49af760e11b81526001600160a01b03808716600483015291925060009161494091879186918b918d16906337e935ee906024016108fd565b9050428411156149675761495681888685612956565b6149609087615a3c565b9550614974565b6149718187615a3c565b95505b61497e89866152e3565b94505050505061480a565b509392505050565b8454600090819081906001600160a01b03166149b557506000915081905080614ae8565b87546001600160a01b03165b6001600160a01b03811615614ae6576040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015614a17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a3b9190615a0d565b90508015614ad457816001600160a01b0316886001600160a01b031603614a6d57614a668782615a3c565b9050600192505b604051631bf49af760e11b81526001600160a01b038084166004830152600091614aa991859185918b91908f16906337e935ee906024016108fd565b90506000614ab78483614db1565b9050614ac38188615a3c565b9650614acf8287615a3c565b955050505b614ade8a836152e3565b9150506149c1565b505b955095509592505050565b855460009081908190614b0e57506000915081905080614da5565b88546000905b8015614da257600081815260028c016020526040812080549091906001600160a01b03808c16911603614b4c57506001935087614d50565b60038201546040516372e3a5b160e01b8152600481018590526001600160a01b03909116906372e3a5b19060240160e060405180830381865afa158015614b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bbb9190615e43565b6080015190508160020160009054906101000a90046001600160a01b03166001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c3a9190615af1565b8015614c5f575081546001600160a01b0316600090815260028d016020526040902054155b15614d505760028201546001600160a01b03858116911614614d505781546040516370a0823160e01b8152306004820152614d3d916001600160a01b03169081906370a0823190602401602060405180830381865afa158015614cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cea9190615a0d565b8a8e6001600160a01b03166337e935ee8760000160009054906101000a90046001600160a01b03166040518263ffffffff1660e01b81526004016108fd91906001600160a01b0391909116815260200190565b60028301546001600160a01b0316945090505b8015614d8a578154600090614d6e906001600160a01b031683614db1565b9050614d7a8189615a3c565b9750614d868288615a3c565b9650505b5050600090815260018b016020526040902054614b14565b50505b96509650969350505050565b600080614dbd84615306565b905042811115614de2576000614dd2826153bd565b9050614dde8482615a62565b9250505b5092915050565b6000614e3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166153c99092919063ffffffff16565b9050805160001480614e5f575080806020019051810190614e5f9190615af1565b610c1e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610741565b614ec88282611889565b610c9d57614ed5816153d8565b614ee08360206153ea565b604051602001614ef1929190615edf565b60408051601f198184030181529082905262461bcd60e51b8252610741916004016159c5565b801580614f915750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614f6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614f8f9190615a0d565b155b614ffc5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610741565b6040516001600160a01b038316602482015260448101829052610c1e90849063095ea7b360e01b90606401612cee565b60008360000154905081604001516001600160a01b0316635fb8f3ed6040518163ffffffff1660e01b8152600401602060405180830381865afa158015615077573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061509b9190615af1565b156150a557600080fd5b80615125575081835560009182526001808401602090815260408085208590556002958601825293849020835181546001600160a01b03199081166001600160a01b039283161783559285015193820193909355938301519484018054821695831695909517909455606090910151600390920180549093169116179055565b60005b81156151e1578184146151e157600082815260028087016020526040918290200154908401516001600160a01b0391821691811682111561519e578261517057858755615184565b600083815260018801602052604090208690555b5050600084815260018601602052604090208290556151e1565b6000848152600188016020526040902054806151d65750505060008281526001860160205260408082208690558582528120556151e1565b939250615128915050565b5050600091825260029283016020908152604092839020825181546001600160a01b039182166001600160a01b03199182161783559284015160018301559383015194810180549585169583169590951790945560609091015160039093018054939092169216919091179055565b805460009061526157506000919050565b81545b80156137a7578161527481615e2a565b6000928352600185016020526040909220549192506152649050565b80546000906001600160a01b03166152aa57506000919050565b81546001600160a01b03165b6001600160a01b038116156137a757816152cf81615e2a565b9250506152dc83826152e3565b90506152b6565b6001600160a01b0380821660009081526001840160205260409020541692915050565b6000816001600160a01b03166379502c556040518163ffffffff1660e01b8152600401608060405180830381865afa158015615346573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061536a9190615b42565b5091949350505050565b60005460ff16611b005760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610741565b6000610ad84283615a4f565b60606129de8484600085615586565b6060610ad86001600160a01b03831660145b606060006153f9836002615a62565b615404906002615a3c565b67ffffffffffffffff81111561541c5761541c615cad565b6040519080825280601f01601f191660200182016040528015615446576020820181803683370190505b509050600360fc1b8160008151811061546157615461615cc3565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061549057615490615cc3565b60200101906001600160f81b031916908160001a90535060006154b4846002615a62565b6154bf906001615a3c565b90505b6001811115615537576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106154f3576154f3615cc3565b1a60f81b82828151811061550957615509615cc3565b60200101906001600160f81b031916908160001a90535060049490941c9361553081615f54565b90506154c2565b50831561186b5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610741565b6060824710156155e75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610741565b600080866001600160a01b031685876040516156039190615f6b565b60006040518083038185875af1925050503d8060008114615640576040519150601f19603f3d011682016040523d82523d6000602084013e615645565b606091505b509150915061294b87838387606083156156c05782516000036156b9576001600160a01b0385163b6156b95760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610741565b50816129de565b6129de83838151156156d55781518083602001fd5b8060405162461bcd60e51b815260040161074191906159c5565b6001600160a01b0381168114610cb257600080fd5b6000806040838503121561571757600080fd5b8235615722816156ef565b946020939093013593505050565b60006020828403121561574257600080fd5b81356001600160e01b03198116811461186b57600080fd5b60006020828403121561576c57600080fd5b813561186b816156ef565b8015158114610cb257600080fd5b6000806040838503121561579857600080fd5b82356157a3816156ef565b915060208301356157b381615777565b809150509250929050565b6000602082840312156157d057600080fd5b5035919050565b600080604083850312156157ea57600080fd5b8235915060208301356157b3816156ef565b600080600080600060a0868803121561581457600080fd5b853561581f816156ef565b9450602086013561582f816156ef565b94979496505050506040830135926060810135926080909101359150565b6020808252825182820181905260009190848201906040850190845b8181101561588557835183529284019291840191600101615869565b50909695505050505050565b60005b838110156158ac578181015183820152602001615894565b50506000910152565b600081518084526158cd816020860160208601615891565b601f01601f19169290920160200192915050565b82151581526040602082015260006129de60408301846158b5565b60008060006040848603121561591157600080fd5b833561591c816156ef565b9250602084013567ffffffffffffffff8082111561593957600080fd5b818601915086601f83011261594d57600080fd5b81358181111561595c57600080fd5b8760208260051b850101111561597157600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b818110156158855783516001600160a01b0316835292840192918401916001016159a0565b60208152600061186b60208301846158b5565b6000806000606084860312156159ed57600080fd5b83356159f8816156ef565b95602085013595506040909401359392505050565b600060208284031215615a1f57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610ad857610ad8615a26565b81810381811115610ad857610ad8615a26565b8082028115828204841417610ad857610ad8615a26565b600082615a9657634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615aad57600080fd5b815161186b816156ef565b6020808252810182905260006001600160fb1b03831115615ad857600080fd5b8260051b80856040850137919091016040019392505050565b600060208284031215615b0357600080fd5b815161186b81615777565b600181811c90821680615b2257607f821691505b6020821081036137a757634e487b7160e01b600052602260045260246000fd5b60008060008060808587031215615b5857600080fd5b845193506020850151615b6a816156ef565b6040860151909350615b7b816156ef565b6060860151909250615b8c816156ef565b939692955090935050565b600060208284031215615ba957600080fd5b815160ff8116811461186b57600080fd5b600181815b80851115615bf5578160001904821115615bdb57615bdb615a26565b80851615615be857918102915b93841c9390800290615bbf565b509250929050565b600082615c0c57506001610ad8565b81615c1957506000610ad8565b8160018114615c2f5760028114615c3957615c55565b6001915050610ad8565b60ff841115615c4a57615c4a615a26565b50506001821b610ad8565b5060208310610133831016604e8410600b8410161715615c78575081810a610ad8565b615c828383615bba565b8060001904821115615c9657615c96615a26565b029392505050565b600061186b60ff841683615bfd565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b602080825282518282018190526000919060409081850190868401855b82811015615d4657815180518552868101516001600160a01b0390811688870152868201518787015260608083015190870152608091820151169085015260a09093019290850190600101615cf6565b5091979650505050505050565b604051601f8201601f1916810167ffffffffffffffff81118282101715615d7c57615d7c615cad565b604052919050565b60006020808385031215615d9757600080fd5b825167ffffffffffffffff80821115615daf57600080fd5b818501915085601f830112615dc357600080fd5b815181811115615dd557615dd5615cad565b8060051b9150615de6848301615d53565b8181529183018401918481019088841115615e0057600080fd5b938501935b83851015615e1e57845182529385019390850190615e05565b98975050505050505050565b600060018201615e3c57615e3c615a26565b5060010190565b600060e08284031215615e5557600080fd5b60405160e0810181811067ffffffffffffffff82111715615e7857615e78615cad565b604052825181526020830151615e8d816156ef565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615ec0816156ef565b60a082015260c0830151615ed381615777565b60c08201529392505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615f17816017850160208801615891565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615f48816028840160208801615891565b01602801949350505050565b600081615f6357615f63615a26565b506000190190565b60008251615f7d818460208701615891565b919091019291505056fe7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55a26469706673582212204858979e1cc3d5ca7b21190dda166390ee2621246bdd11b671082dfeaed851a064736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e60000000000000000000000003c6b0398eed7dafcb3c13d482400329a6e25acd200000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d960000000000000000000000003210cf0892b794a9403f958fdb94ae2e13ac451f00000000000000000000000021fc7b250ccaeecdb2abb38e04617d1f24d9877200000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000000000000003b538000000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001553686f7265776f6f6473205072696d6520555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000001674737653686f7265776f6f64735072696d655553444300000000000000000000
-----Decoded View---------------
Arg [0] : _name (string): Shorewoods Prime USDC
Arg [1] : _symbol (string): tsvShorewoodsPrimeUSDC
Arg [2] : _params (tuple):
Arg [1] : _asset (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _yearnVault (address): 0x73edDFa87C71ADdC275c2b9890f5c3a8480bC9E6
Arg [3] : _discountRateAdapter (address): 0x3C6b0398eEd7dAfcb3C13d482400329a6e25Acd2
Arg [4] : _eventEmitter (address): 0x81d52833696C9CF99db3A0D3A586b480287e8d96
Arg [5] : _governorAddress (address): 0x3210CF0892b794A9403f958FDB94AE2E13Ac451F
Arg [6] : _termController (address): 0x21FC7B250CCAeECDb2abb38e04617D1f24D98772
Arg [7] : _repoTokenConcentrationLimit (uint256): 500000000000000000
Arg [8] : _timeToMaturityThreshold (uint256): 3888000
Arg [9] : _requiredReserveRatio (uint256): 200000000000000000
Arg [10] : _discountRateMarkup (uint256): 0
-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [1] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [2] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [3] : 00000000000000000000000073eddfa87c71addc275c2b9890f5c3a8480bc9e6
Arg [4] : 0000000000000000000000003c6b0398eed7dafcb3c13d482400329a6e25acd2
Arg [5] : 00000000000000000000000081d52833696c9cf99db3a0d3a586b480287e8d96
Arg [6] : 0000000000000000000000003210cf0892b794a9403f958fdb94ae2e13ac451f
Arg [7] : 00000000000000000000000021fc7b250ccaeecdb2abb38e04617d1f24d98772
Arg [8] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [9] : 00000000000000000000000000000000000000000000000000000000003b5380
Arg [10] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000015
Arg [13] : 53686f7265776f6f6473205072696d6520555344430000000000000000000000
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000016
Arg [15] : 74737653686f7265776f6f64735072696d655553444300000000000000000000
Loading...
Loading
Loading...
Loading

Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.