ERC-20
Overview
Max Total Supply
10 ERC20 ***
Holders
1
Market
Price
$0.00 @ 0.000000 S
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Multistrategy
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity 0.8.27; import { IERC20, IERC4626, ERC20, ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { MultistrategyManageable } from "src/abstracts/MultistrategyManageable.sol"; import { IMultistrategy } from "interfaces/infra/multistrategy/IMultistrategy.sol"; import { IStrategyAdapter } from "interfaces/infra/multistrategy/IStrategyAdapter.sol"; import { Errors } from "src/infra/libraries/Errors.sol"; contract Multistrategy is IMultistrategy, MultistrategyManageable, ERC4626, ReentrancyGuard { using SafeERC20 for IERC20; using Math for uint256; /// @dev How much time it takes for the profit of a strategy to be unlocked. uint256 public constant PROFIT_UNLOCK_TIME = 3 days; /// @dev Used for locked profit calculations. uint256 public constant DEGRADATION_COEFFICIENT = 1 ether; /// @notice OpenZeppelin decimals offset used by the ERC4626 implementation. /// @dev Calculated to be max(0, 18 - underlyingDecimals) at construction, so the initial conversion rate maximizes /// precision between shares and assets. uint8 public immutable DECIMALS_OFFSET; /// @inheritdoc IMultistrategy uint256 public lastReport; /// @inheritdoc IMultistrategy uint256 public lockedProfit; /// @inheritdoc IMultistrategy uint256 public immutable lockedProfitDegradation; /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ /// @dev Transfers ownership to the deployer of this contract /// @param _asset Address of the token used in this Multistrategy /// @param _manager Address of the initial Multistrategy manager /// @param _protocolFeeRecipient Address that will receive the performance fees /// @param _name Name of this Multistrategy receipt token /// @param _symbol Symbol of this Multistrategy receipt token constructor( address _asset, address _manager, address _protocolFeeRecipient, string memory _name, string memory _symbol ) MultistrategyManageable(msg.sender, _manager, _protocolFeeRecipient) ERC4626(IERC20(_asset)) ERC20(_name, _symbol) { DECIMALS_OFFSET = uint8(Math.max(0, uint256(18) - IERC20Metadata(_asset).decimals())); performanceFee = 1000; lastReport = block.timestamp; lockedProfitDegradation = DEGRADATION_COEFFICIENT / PROFIT_UNLOCK_TIME; } /*////////////////////////////////////////////////////////////////////////// USER FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IERC4626 function totalAssets() public view override returns (uint256) { return _liquidity() + totalDebt; } /// @inheritdoc IERC4626 /// @dev Limited by the deposit limit function maxDeposit(address) public view override returns (uint256) { if(totalAssets() >= depositLimit) { return 0; } else { return depositLimit - totalAssets(); } } /// @inheritdoc IERC4626 /// @dev Limited by the deposit limit function maxMint(address _receiver) public view override returns (uint256) { return convertToShares(maxDeposit(_receiver)); } /// @inheritdoc IERC4626 function previewWithdraw(uint256 _assets) public view override returns (uint256) { uint256 shares = _convertToShares(_assets, Math.Rounding.Ceil); if(_assets <= _liquidity()) { return shares; } else { if(slippageLimit == MAX_BPS) return type(uint256).max; // Return the number of shares required at the current rate, accounting for slippage. return shares.mulDiv(MAX_BPS, MAX_BPS - slippageLimit, Math.Rounding.Ceil); } } /// @inheritdoc IERC4626 function previewRedeem(uint256 _shares) public view override returns (uint256) { uint256 assets = _convertToAssets(_shares, Math.Rounding.Floor); if(assets <= _liquidity()) { return assets; } else { // Return the number of assets redeemable at the maximum permitted slippage. return assets.mulDiv(MAX_BPS - slippageLimit, MAX_BPS, Math.Rounding.Floor); } } /// @inheritdoc IMultistrategy function pricePerShare() external view returns (uint256) { return convertToAssets(1 ether); } /// @inheritdoc IMultistrategy function creditAvailable(address _strategy) external view returns (uint256) { return _creditAvailable(_strategy); } /// @inheritdoc IMultistrategy function debtExcess(address _strategy) external view returns (uint256) { return _debtExcess(_strategy); } /// @inheritdoc IMultistrategy function strategyTotalDebt(address _strategy) external view returns (uint256) { return strategies[_strategy].totalDebt; } function currentPnL() external view returns (uint256, uint256) { return _currentPnL(); } /*////////////////////////////////////////////////////////////////////////// USER FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IERC4626 function deposit(uint256 _assets, address _receiver) public override whenNotPaused whenNotRetired nonReentrant returns (uint256) { uint256 maxAssets = maxDeposit(_receiver); require(_assets <= maxAssets, ERC4626ExceededMaxDeposit(_receiver, _assets, maxAssets)); uint256 shares = previewDeposit(_assets); _deposit(msg.sender, _receiver, _assets, shares); return shares; } /// @inheritdoc IERC4626 function mint(uint256 _shares, address _receiver) public override whenNotPaused whenNotRetired nonReentrant returns (uint256) { uint256 maxShares = maxMint(_receiver); require(_shares <= maxShares, ERC4626ExceededMaxMint(_receiver, _shares, maxShares)); uint256 assets = previewMint(_shares); _deposit(msg.sender, _receiver, assets, _shares); return assets; } /// @inheritdoc IERC4626 function withdraw(uint256 _assets, address _receiver, address _owner) public override whenNotPaused nonReentrant returns (uint256) { uint256 maxAssets = maxWithdraw(_owner); require(_assets <= maxAssets, ERC4626ExceededMaxWithdraw(_owner, _assets, maxAssets)); uint256 maxShares = previewWithdraw(_assets); (, uint256 shares) = _withdraw(msg.sender, _receiver, _owner, _assets, maxShares, false); require(shares <= maxShares, Errors.SlippageCheckFailed(maxShares, shares)); return shares; } /// @inheritdoc IERC4626 function redeem(uint256 _shares, address _receiver, address _owner) public override whenNotPaused nonReentrant returns (uint256) { uint256 maxShares = maxRedeem(_owner); require(_shares <= maxShares, ERC4626ExceededMaxRedeem(_owner, _shares, maxShares)); uint256 minAssets = previewRedeem(_shares); (uint256 assets, ) = _withdraw(msg.sender, _receiver, _owner, minAssets, _shares, true); require(assets >= minAssets, Errors.SlippageCheckFailed(minAssets, assets)); return assets; } /// @inheritdoc IMultistrategy function requestCredit() external whenNotPaused onlyActiveStrategy(msg.sender) returns (uint256) { return _requestCredit(); } /// @inheritdoc IMultistrategy function strategyReport(uint256 _debtRepayment, uint256 _gain, uint256 _loss) external whenNotPaused onlyActiveStrategy(msg.sender) { _report(_debtRepayment, _gain, _loss); } /// @inheritdoc IMultistrategy function rescueToken(address _token, address _recipient) external onlyGuardian { _rescueToken(_token, _recipient); } /*////////////////////////////////////////////////////////////////////////// INTERNAL CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Internal view function to retrieve the current liquidity of the contract. /// @return The current liquidity (balance of the asset) of the contract. function _liquidity() internal view returns (uint256) { return IERC20(asset()).balanceOf(address(this)); } /// @notice Converts a given amount of assets to shares, with specified rounding. /// @param _assets The amount of assets to convert to shares. /// @param rounding The rounding direction to apply during the conversion. /// @return The number of shares corresponding to the given amount of assets. function _convertToShares(uint256 _assets, Math.Rounding rounding) internal view override returns (uint256) { return _assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), _freeFunds() + 1, rounding); } /// @notice Convert a given amount of shares to assets, with specified rounding. /// @param _shares The number of shares to convert to assets. /// @param rounding The rounding direction to apply during the conversion. /// @return The amount of assets corresponding to the given number of shares. function _convertToAssets(uint256 _shares, Math.Rounding rounding) internal view override returns (uint256) { return _shares.mulDiv(_freeFunds() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /// @notice Calculates the available credit for a strategy. /// /// This function performs the following actions: /// - Determines the total assets and debt limits for both the multi-strategy and the specific strategy. /// - Checks if the strategy or the multi-strategy has exceeded their respective debt limits, in which case no new credit is offered. /// - Calculates the potential credit as the difference between the strategy's debt limit and its current debt. /// - Limits the potential credit by the maximum available credit of the multi-strategy. /// - Ensures the potential credit is within the strategy's minimum and maximum debt delta bounds. /// - Returns zero if the available credit is below the strategy's minimum debt delta. /// - Returns the available credit, ensuring it does not exceed the strategy's maximum debt delta. /// /// @param _strategy The address of the strategy for which to determine the available credit. /// @return The amount of credit available for the given strategy. function _creditAvailable(address _strategy) internal view returns (uint256) { uint256 mult_totalAssets = totalAssets(); uint256 mult_debtLimit = debtRatio.mulDiv(mult_totalAssets, MAX_BPS); uint256 mult_totalDebt = totalDebt; uint256 strat_debtLimit = strategies[_strategy].debtRatio.mulDiv(mult_totalAssets, MAX_BPS); uint256 strat_totalDebt = strategies[_strategy].totalDebt; uint256 strat_minDebtDelta = strategies[_strategy].minDebtDelta; uint256 strat_maxDebtDelta = strategies[_strategy].maxDebtDelta; if(strat_totalDebt >= strat_debtLimit || mult_totalDebt >= mult_debtLimit){ return 0; } uint256 credit = strat_debtLimit - strat_totalDebt; uint256 maxAvailableCredit = mult_debtLimit - mult_totalDebt; credit = Math.min(credit, maxAvailableCredit); // Bound to the minimum and maximum borrow limits if(credit < strat_minDebtDelta) { return 0; } else { return Math.min(credit, strat_maxDebtDelta); } } /// @notice Calculates the excess debt of a strategy. /// /// This function performs the following actions: /// - If the overall debt ratio is zero, it returns the total debt of the strategy as excess debt. /// - Calculates the strategy's debt limit based on its debt ratio and the total assets. /// - If the strategy's total debt is less than or equal to its debt limit, it returns zero indicating no excess debt. /// - If the strategy's total debt exceeds its debt limit, it returns the difference as the excess debt. /// /// @param _strategy The address of the strategy for which to determine the debt excess. /// @return The amount of excess debt for the given strategy. function _debtExcess(address _strategy) internal view returns (uint256) { if(debtRatio == 0) { return strategies[_strategy].totalDebt; } uint256 strat_debtLimit = strategies[_strategy].debtRatio.mulDiv(totalAssets(), MAX_BPS); uint256 strat_totalDebt = strategies[_strategy].totalDebt; if(strat_totalDebt <= strat_debtLimit) { return 0; } else { return strat_totalDebt - strat_debtLimit; } } /// @notice Calculates the free funds available in the contract. /// @return The amount of free funds available. function _freeFunds() internal view returns (uint256) { return totalAssets() - _calculateLockedProfit(); } /// @notice Calculate the current locked profit. /// /// This function performs the following actions: /// - Calculates the locked funds ratio based on the time elapsed since the last report and the locked profit degradation rate. /// - If the locked funds ratio is less than the degradation coefficient, it computes the remaining locked profit by reducing it proportionally. /// - If the locked funds ratio is greater than or equal to the degradation coefficient, it returns zero indicating no locked profit remains. /// /// @return The calculated current locked profit. function _calculateLockedProfit() internal view returns (uint256) { uint256 lockedFundsRatio = (block.timestamp - lastReport) * lockedProfitDegradation; if(lockedFundsRatio < DEGRADATION_COEFFICIENT) { return lockedProfit - lockedFundsRatio.mulDiv(lockedProfit, DEGRADATION_COEFFICIENT); } return 0; } /** * @notice Calculates the current profit and loss (PnL) across all active strategies. * * This function performs the following actions: * - Iterates through the `withdrawOrder` array, which defines the order in which strategies are withdrawn from. * - For each strategy in the `withdrawOrder`: * - If the strategy address is zero, it breaks the loop, indicating the end of the list. * - If the strategy has no debt, it skips to the next strategy. * - Otherwise, it retrieves the current profit and loss (PnL) from the strategy by calling `currentPnL`. * - Adds the strategy's profit to the total profit, after deducting the performance fee. * - Adds the strategy's loss to the total loss. * - Returns the total profit and total loss across all active strategies. * * @return totalProfit The total profit across all active strategies, after deducting the performance fee. * @return totalLoss The total loss across all active strategies. */ function _currentPnL() internal view returns (uint256, uint256) { if (activeStrategies == 0) return (0, 0); uint256 totalProfit = 0; uint256 totalLoss = 0; for(uint8 i = 0; i < activeStrategies; ++i){ address strategy = withdrawOrder[i]; if(strategy == address(0)) break; if(strategies[strategy].totalDebt == 0) continue; (uint256 gain, uint256 loss) = IStrategyAdapter(strategy).currentPnL(); totalProfit += gain.mulDiv(MAX_BPS - performanceFee, MAX_BPS); totalLoss += loss; } return (totalProfit, totalLoss); } function _decimalsOffset() internal view override returns (uint8) { return DECIMALS_OFFSET; } /*////////////////////////////////////////////////////////////////////////// INTERNAL NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Handles deposits into the contract. /// /// This function performs the following actions: /// - Validates that the receiver address is not zero or the contract address itself. /// - Ensures that the deposited amount is greater than zero. /// - Transfers the assets from the caller to the contract. /// - Mints the corresponding shares for the receiver. /// - Emits a `Deposit` event with the caller, receiver, amount of assets, and number of shares. /// /// @param _caller The address of the entity initiating the deposit. /// @param _receiver The address of the recipient to receive the shares. /// @param _assets The amount of assets being deposited. /// @param _shares The number of shares to be minted for the receiver. function _deposit(address _caller, address _receiver, uint256 _assets, uint256 _shares) internal override { require(_receiver != address(0) && _receiver != address(this), Errors.InvalidAddress(_receiver)); require(_assets > 0, Errors.ZeroAmount(_assets)); IERC20(asset()).safeTransferFrom(_caller, address(this), _assets); _mint(_receiver, _shares); emit Deposit(_caller, _receiver, _assets, _shares); } /// @notice Handles withdrawals from the contract. /// /// This function performs the following actions: /// - If the caller is not the owner, it checks and spends the allowance for the withdrawal. /// - Ensures that the amount to be withdrawn is greater than zero. /// - If the requested withdrawal amount exceeds the available liquidity, it withdraws the necessary amount from the strategies in the withdrawal order. /// - Iterates through the withdrawal queue, withdrawing from each strategy until the liquidity requirement is met or the queue is exhausted. /// - Updates the total debt of both the strategy and the contract as assets are withdrawn. /// - Requests the strategy to report, accounting for potential gains or losses. /// - Reverts if the withdrawal process does not result in sufficient liquidity. /// - Burns the corresponding shares and transfers the requested assets to the receiver. /// - Emits a `Withdraw` event with the caller, receiver, owner, amount of assets withdrawn, and shares burned. /// /// @param _caller The address of the entity initiating the withdrawal. /// @param _receiver The address of the recipient to receive the withdrawn assets. /// @param _owner The address of the owner of the shares being withdrawn. /// @param _assets The amount of assets to withdraw. /// @param _shares The amount of shares to burn. /// @param _consumeAllShares True if all `_shares` should be used to withdraw. False if it should withdraw just `_assets`. /// @return The number of assets withdrawn and the shares burned as a result of the withdrawal. function _withdraw( address _caller, address _receiver, address _owner, uint256 _assets, uint256 _shares, bool _consumeAllShares ) internal returns (uint256, uint256) { require(_shares > 0, Errors.ZeroAmount(_shares)); if (_caller != _owner) { _spendAllowance(_owner, _caller, _shares); } uint256 assets = _consumeAllShares ? _convertToAssets(_shares, Math.Rounding.Floor) : _assets; if(assets > _liquidity()) { for(uint8 i = 0; i <= withdrawOrder.length; ++i){ address strategy = withdrawOrder[i]; // We reached the end of the withdraw queue and assets are still higher than the liquidity require(strategy != address(0), Errors.InsufficientLiquidity(assets, _liquidity())); // We can't withdraw from a strategy more than what it has asked as credit. uint256 assetsToWithdraw = Math.min(assets - _liquidity(), strategies[strategy].totalDebt); if(assetsToWithdraw == 0) continue; uint256 withdrawn = IStrategyAdapter(strategy).withdraw(assetsToWithdraw); strategies[strategy].totalDebt -= withdrawn; totalDebt -= withdrawn; IStrategyAdapter(strategy).askReport(); // Update assets, as a loss could have been reported and user should get less assets for // the same amount of shares. if(_consumeAllShares) assets = _convertToAssets(_shares, Math.Rounding.Floor); if(assets <= _liquidity()) break; } } uint256 shares = _consumeAllShares ? _shares : _convertToShares(assets, Math.Rounding.Ceil); _burn(_owner, shares); IERC20(asset()).safeTransfer(_receiver, assets); emit Withdraw(_caller, _receiver, _owner, assets, shares); return (assets, shares); } /// @notice Requests credit for an active strategy. /// /// This function performs the following actions: /// - Calculates the available credit for the strategy using `_creditAvailable`. /// - If credit is available, it updates the total debt for the strategy and the multistrategy contract. /// - Transfers the calculated credit amount to the strategy. /// /// Emits a `CreditRequested` event. /// /// @dev This function should be called only by active strategies when they need to request credit. function _requestCredit() internal returns (uint256){ uint256 credit = _creditAvailable(msg.sender); if(credit > 0) { strategies[msg.sender].totalDebt += credit; totalDebt += credit; IERC20(asset()).safeTransfer(msg.sender, credit); emit CreditRequested(msg.sender, credit); } return credit; } /// @notice Reports the performance of a strategy. /// /// This function performs the following actions: /// - Validates that the reporting strategy does not claim both a gain and a loss simultaneously. /// - Checks that the strategy has sufficient tokens to cover the debt repayment and the gain. /// - If there is a loss, it realizes the loss. /// - Calculates and deducts the performance fee from the gain. /// - Determines the excess debt of the strategy. /// - Adjusts the strategy's and contract's total debt accordingly. /// - Calculates and updates the new locked profit after accounting for any losses. /// - Updates the reporting timestamps for the strategy and the contract. /// - Transfers the debt repayment and the gains to this contract. /// /// Emits a `StrategyReported` event. /// /// @param _debtRepayment The amount of debt being repaid by the strategy. /// @param _gain The amount of profit reported by the strategy. /// @param _loss The amount of loss reported by the strategy. function _report(uint256 _debtRepayment, uint256 _gain, uint256 _loss) internal { uint256 strategyBalance = IERC20(asset()).balanceOf(msg.sender); require(!(_gain > 0 && _loss > 0), Errors.GainLossMismatch()); require(strategyBalance >= _debtRepayment + _gain, Errors.InsufficientBalance(strategyBalance, _debtRepayment + _gain)); uint256 profit = 0; uint256 feesCollected = 0; if(_loss > 0) _reportLoss(msg.sender, _loss); if(_gain > 0) { feesCollected = _gain.mulDiv(performanceFee, MAX_BPS); profit = _gain - feesCollected; } uint256 debtToRepay = Math.min(_debtRepayment, _debtExcess(msg.sender)); if(debtToRepay > 0) { strategies[msg.sender].totalDebt -= debtToRepay; totalDebt -= debtToRepay; } uint256 newLockedProfit = _calculateLockedProfit() + profit; if(newLockedProfit > _loss) { lockedProfit = newLockedProfit - _loss; } else { lockedProfit = 0; } strategies[msg.sender].lastReport = block.timestamp; lastReport = block.timestamp; if(debtToRepay + _gain > 0) IERC20(asset()).safeTransferFrom(msg.sender, address(this), debtToRepay + _gain); if(feesCollected > 0) IERC20(asset()).safeTransfer(protocolFeeRecipient, feesCollected); emit StrategyReported(msg.sender, debtToRepay, profit, _loss); } /// @notice Reports a loss for a strategy. /// /// This function performs the following actions: /// - Validates that the loss reported by the strategy does not exceed its total debt. /// - Updates the strategy's total loss by adding the reported loss. /// - Reduces the strategy's total debt by the reported loss. /// - Adjusts the contract's total debt by reducing it with the reported loss. /// /// @param _strategy The address of the strategy reporting the loss. /// @param _loss The amount of loss reported by the strategy. function _reportLoss(address _strategy, uint256 _loss) internal { require(_loss <= strategies[_strategy].totalDebt, Errors.InvalidStrategyLoss()); strategies[_strategy].totalLoss += _loss; strategies[_strategy].totalDebt -= _loss; totalDebt -= _loss; } /// @notice Rescues tokens from the contract. /// /// This function performs the following actions: /// - Retrieves the balance of the specified token in the contract. /// - Transfers the entire balance of the specified token to the recipient address. /// /// @param _token The address of the token to be rescued. /// @param _recipient The address to receive the rescued tokens. function _rescueToken(address _token, address _recipient) internal { require(_token != asset(), Errors.InvalidAddress(_token)); uint256 amount = IERC20(_token).balanceOf(address(this)); IERC20(_token).safeTransfer(_recipient, amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.20; import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol"; import {SafeERC20} from "../utils/SafeERC20.sol"; import {IERC4626} from "../../../interfaces/IERC4626.sol"; import {Math} from "../../../utils/math/Math.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * [CAUTION] * ==== * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()` * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more * expensive than it is profitable. More details about the underlying math can be found * xref:erc4626.adoc#inflation-attack[here]. * * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the * `_convertToShares` and `_convertToAssets` functions. * * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide]. * ==== */ abstract contract ERC4626 is ERC20, IERC4626 { using Math for uint256; IERC20 private immutable _asset; uint8 private immutable _underlyingDecimals; /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ constructor(IERC20 asset_) { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _underlyingDecimals = success ? assetDecimals : 18; _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeCall(IERC20Metadata.decimals, ()) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals. * * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { return _underlyingDecimals + _decimalsOffset(); } /** @dev See {IERC4626-asset}. */ function asset() public view virtual returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Floor); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual returns (uint256) { uint256 maxAssets = maxDeposit(receiver); if (assets > maxAssets) { revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); } uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual returns (uint256) { uint256 maxShares = maxMint(receiver); if (shares > maxShares) { revert ERC4626ExceededMaxMint(receiver, shares, maxShares); } uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { uint256 maxAssets = maxWithdraw(owner); if (assets > maxAssets) { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { uint256 maxShares = maxRedeem(owner); if (shares > maxShares) { revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); } uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding); } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /** * @dev Deposit/mint common workflow. */ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _decimalsOffset() internal view virtual returns (uint8) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../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 An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @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.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ 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 v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @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 towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (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 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) 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. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 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. uint256 twos = denominator & (0 - denominator); 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 (unsignedRoundsUp(rounding) && 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 * towards zero. * * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity 0.8.27; import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import { MultistrategyAdminable } from "src/abstracts/MultistrategyAdminable.sol"; import { IMultistrategyManageable } from "interfaces/infra/multistrategy/IMultistrategyManageable.sol"; import { IStrategyAdapter } from "interfaces/infra/multistrategy/IStrategyAdapter.sol"; import { MStrat } from "src/types/DataTypes.sol"; import { Errors } from "src/infra/libraries/Errors.sol"; abstract contract MultistrategyManageable is IMultistrategyManageable, MultistrategyAdminable { /// @dev Maximum amount of different strategies this contract can deposit into uint8 constant MAXIMUM_STRATEGIES = 10; /// @dev Maximum basis points (10_000 = 100%) uint256 constant MAX_BPS = 10_000; /// @dev Maximum performance fee that the owner can set is 20% uint256 constant MAX_PERFORMANCE_FEE = 2_000; /// @inheritdoc IMultistrategyManageable address public protocolFeeRecipient; /// @inheritdoc IMultistrategyManageable uint256 public performanceFee; /// @inheritdoc IMultistrategyManageable uint256 public depositLimit; /// @inheritdoc IMultistrategyManageable uint256 public debtRatio; /// @inheritdoc IMultistrategyManageable uint256 public totalDebt; /// @inheritdoc IMultistrategyManageable uint256 public slippageLimit; /// @inheritdoc IMultistrategyManageable uint8 public activeStrategies; /// @inheritdoc IMultistrategyManageable bool public retired; /*////////////////////////////////////////////////////////////////////////// PRIVATE STORAGE //////////////////////////////////////////////////////////////////////////*/ /// @dev Strategy parameters mapped by the strategy address mapping(address strategyAddress => MStrat.StrategyParams strategyParameters) public strategies; /// @dev Order that `_withdraw()` uses to determine which strategy pull the funds from // The first time a zero address is encountered, it stops withdrawing, so it is // possible that there isn't enough to withdraw if the amount of strategies in // `withdrawOrder` is smaller than the amount of active strategies. address[] public withdrawOrder; /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ /// @dev Initial owner is the deployer of the multistrategy. /// @param _owner Address of the initial Multistrategy owner. /// @param _manager Address of the initial Multistrategy manager. /// @param _protocolFeeRecipient Address that will receive the performance fee. constructor( address _owner, address _manager, address _protocolFeeRecipient ) MultistrategyAdminable(_owner, _manager) { require(_protocolFeeRecipient != address(0), Errors.ZeroAddress()); protocolFeeRecipient = _protocolFeeRecipient; withdrawOrder = new address[](MAXIMUM_STRATEGIES); } /*////////////////////////////////////////////////////////////////////////// MODIFIER //////////////////////////////////////////////////////////////////////////*/ /// @dev Reverts if `_strategy` is not active. /// @param _strategy Address of the strategy to check if it is active. modifier onlyActiveStrategy(address _strategy) { require(strategies[_strategy].activation > 0, Errors.StrategyNotActive(_strategy)); _; } /// @dev Reverts if the multistrategy has been retired / eol. modifier whenNotRetired() { require(retired == false, Errors.Retired()); _; } /*////////////////////////////////////////////////////////////////////////// USER FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IMultistrategyManageable function getWithdrawOrder() external view returns (address[] memory) { return withdrawOrder; } /// @inheritdoc IMultistrategyManageable function getStrategyParameters(address _strategy) external view returns (MStrat.StrategyParams memory) { return strategies[_strategy]; } /*////////////////////////////////////////////////////////////////////////// USER FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IMultistrategyManageable function setProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner { require(_protocolFeeRecipient != address(0), Errors.ZeroAddress()); protocolFeeRecipient = _protocolFeeRecipient; emit ProtocolFeeRecipientSet(protocolFeeRecipient); } /// @inheritdoc IMultistrategyManageable function setPerformanceFee(uint256 _performanceFee) external onlyOwner { require(_performanceFee <= MAX_PERFORMANCE_FEE, Errors.ExcessiveFee(_performanceFee)); performanceFee = _performanceFee; emit PerformanceFeeSet(performanceFee); } /// @inheritdoc IMultistrategyManageable function setDepositLimit(uint256 _depositLimit) external onlyManager { depositLimit = _depositLimit; emit DepositLimitSet(depositLimit); } function setSlippageLimit(uint256 _slippageLimit) external onlyManager { require(_slippageLimit <= MAX_BPS, Errors.SlippageLimitExceeded(_slippageLimit)); slippageLimit = _slippageLimit; emit SlippageLimitSet(slippageLimit); } /// @inheritdoc IMultistrategyManageable function setWithdrawOrder(address[] memory _strategies) external onlyManager { require(_validateStrategyOrder(_strategies), Errors.InvalidWithdrawOrder()); withdrawOrder = _strategies; emit WithdrawOrderSet(); } /// @inheritdoc IMultistrategyManageable function addStrategy( address _strategy, uint256 _debtRatio, uint256 _minDebtDelta, uint256 _maxDebtDelta ) external onlyOwner { require(activeStrategies < MAXIMUM_STRATEGIES, Errors.MaximumAmountStrategies()); require(_strategy != address(0) && _strategy != address(this), Errors.InvalidAddress(_strategy)); require(strategies[_strategy].activation == 0, Errors.StrategyAlreadyActive(_strategy)); require(IERC4626(address(this)).asset() == IStrategyAdapter(_strategy).asset(), Errors.AssetMismatch(IERC4626(address(this)).asset(), IStrategyAdapter(_strategy).asset())); require(debtRatio + _debtRatio <= MAX_BPS, Errors.DebtRatioAboveMaximum(debtRatio + _debtRatio)); require(_minDebtDelta <= _maxDebtDelta, Errors.InvalidDebtDelta()); strategies[_strategy] = MStrat.StrategyParams({ activation: block.timestamp, debtRatio: _debtRatio, lastReport: block.timestamp, minDebtDelta: _minDebtDelta, maxDebtDelta: _maxDebtDelta, totalDebt: 0, totalGain: 0, totalLoss: 0 }); debtRatio += _debtRatio; withdrawOrder[MAXIMUM_STRATEGIES - 1] = _strategy; ++activeStrategies; _organizeWithdrawOrder(); emit StrategyAdded(_strategy); } /// @inheritdoc IMultistrategyManageable function retireStrategy(address _strategy) external onlyManager onlyActiveStrategy(_strategy) { debtRatio -= strategies[_strategy].debtRatio; strategies[_strategy].debtRatio = 0; emit StrategyRetired(_strategy); } /// @inheritdoc IMultistrategyManageable function removeStrategy(address _strategy) external onlyManager onlyActiveStrategy(_strategy) { require(strategies[_strategy].debtRatio == 0, Errors.StrategyNotRetired()); require(strategies[_strategy].totalDebt == 0, Errors.StrategyWithOutstandingDebt()); for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) { if(withdrawOrder[i] == _strategy) { withdrawOrder[i] = address(0); strategies[_strategy].activation = 0; --activeStrategies; _organizeWithdrawOrder(); emit StrategyRemoved(_strategy); return; } } } /// @inheritdoc IMultistrategyManageable function setStrategyDebtRatio(address _strategy, uint256 _debtRatio) external onlyManager onlyActiveStrategy(_strategy) { uint256 newDebtRatio = debtRatio - strategies[_strategy].debtRatio + _debtRatio; require(newDebtRatio <= MAX_BPS, Errors.DebtRatioAboveMaximum(newDebtRatio)); debtRatio = newDebtRatio; strategies[_strategy].debtRatio = _debtRatio; emit StrategyDebtRatioSet(_strategy, _debtRatio); } /// @inheritdoc IMultistrategyManageable function setStrategyMinDebtDelta(address _strategy, uint256 _minDebtDelta) external onlyManager onlyActiveStrategy(_strategy) { require(strategies[_strategy].maxDebtDelta >= _minDebtDelta, Errors.InvalidDebtDelta()); strategies[_strategy].minDebtDelta = _minDebtDelta; emit StrategyMinDebtDeltaSet(_strategy, _minDebtDelta); } /// @inheritdoc IMultistrategyManageable function setStrategyMaxDebtDelta(address _strategy, uint256 _maxDebtDelta) external onlyManager onlyActiveStrategy(_strategy) { require(strategies[_strategy].minDebtDelta <= _maxDebtDelta, Errors.InvalidDebtDelta()); strategies[_strategy].maxDebtDelta = _maxDebtDelta; emit StrategyMaxDebtDeltaSet(_strategy, _maxDebtDelta); } /// @inheritdoc IMultistrategyManageable function retire() external onlyGuardian { retired = true; emit MultistrategyRetired(); } /*////////////////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Validates the order of strategies for withdrawals. /// /// This function performs the following actions: /// - Ensures the length of the provided strategies array matches the maximum number of strategies. /// - Iterates through the provided strategies to validate each one: /// - Checks that non-zero addresses correspond to active strategies. /// - Ensures there are no duplicate strategies in the provided array. /// - If an address in the provided array is zero, it checks that all subsequent addresses are also zero. /// /// @param _strategies The array of strategy addresses to validate. /// @return True if the order is valid. False if not valid. function _validateStrategyOrder(address[] memory _strategies) internal view returns (bool) { if(_strategies.length != MAXIMUM_STRATEGIES) return false; for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) { address strategy = _strategies[i]; if(strategy != address(0)) { if(strategies[strategy].activation == 0) return false; // Start to check on the next strategy for(uint8 j = 0; j < MAXIMUM_STRATEGIES; ++j) { // Check that the strategy isn't duplicate if(i != j && strategy == _strategies[j]) return false; } } else { // Check that the rest of the addresses are address(0) for(uint8 j = i + 1; j < MAXIMUM_STRATEGIES; ++j) { if(_strategies[j] != address(0)) return false; } return true; } } return true; } /// @notice Organizes the withdraw order by removing gaps and shifting strategies. /// /// This function performs the following actions: /// - Iterates through the withdraw order array. /// - For each strategy, if it encounters an empty slot (address(0)), it shifts subsequent strategies up to fill the gap. /// - Ensures that any empty slots are moved to the end of the array. function _organizeWithdrawOrder() internal { uint8 position = 0; for(uint8 i = 0; i < MAXIMUM_STRATEGIES; ++i) { address strategy = withdrawOrder[i]; if(strategy == address(0)) { ++position; } else if (position > 0) { withdrawOrder[i - position] = strategy; withdrawOrder[i] = address(0); } } } }
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity ^0.8.27; import { IMultistrategyManageable } from "interfaces/infra/multistrategy/IMultistrategyManageable.sol"; interface IMultistrategy is IMultistrategyManageable { /// @notice Emitted when an account has made a deposit. /// @param amount Amount of asset that has been deposited. /// @param recipient Address that will receive the receipt tokens. event Deposit(uint256 amount, address indexed recipient); /// @notice Emitted when an account has made a withdraw. /// @param amount Amount of shares that have been withdrawn. event Withdraw(uint256 amount); /// @notice Emitted when a strategy has requested a credit. /// @param strategy Address of the strategy that requested the credit. /// @param amount Amount of credit that has been granted to the strategy. event CreditRequested(address indexed strategy, uint256 amount); /// @notice Emitted when a strategy has submitted a report. /// @param strategy Address of the strategy that has submitted the report. /// @param debtRepaid Amount of debt that has been repaid by the strategy. /// @param gain Amount of gain that the strategy has reported. /// @param loss Amount of loss that the strategy has reported. event StrategyReported(address indexed strategy, uint256 debtRepaid, uint256 gain, uint256 loss); /// @notice Timestamp of the last report made by a strategy. function lastReport() external view returns (uint256); /// @notice Amount of tokens that are locked as "locked profit" and can't be withdrawn. function lockedProfit() external view returns (uint256); /// @notice Rate at which the locked profit gets unlocked per second. function lockedProfitDegradation() external view returns (uint256); /// @notice Returns the value of a share in `asset` value. function pricePerShare() external view returns (uint256); /// @notice Returns the amount of tokens a strategy can borrow from this Multistrategy. /// @param strategy Address of the strategy we want to know the credit available for. function creditAvailable(address strategy) external view returns (uint256); /// @notice Returns the excess of debt a strategy currently holds. /// @param strategy Address of the strategy we want to know if it has any debt excess. function debtExcess(address strategy) external view returns (uint256); /// @notice Returns the total debt of `strategy`. /// @param strategy Address of the strategy we want to know the `totalDebt`. function strategyTotalDebt(address strategy) external view returns (uint256); /// @notice Returns the aggregate PnL of all strategies at max slippage. function currentPnL() external view returns (uint256, uint256); /// @notice Send the available credit of the caller to the caller. /// @dev Reverts if the caller is *NOT* an active strategy function requestCredit() external returns (uint256); /// @notice Report the profit or loss of a strategy along any debt the strategy is willing to pay back. /// @dev Can only be called by an active strategy. /// @param _debtRepayment Amount that the strategy will send back to the multistrategy as debt repayment. /// @param _gain Amount that the strategy has realized as a gain since the last report and will send it /// to this Multistrategy as earnings. /// @param _loss Amount that the strategy has realized as a loss since the last report. function strategyReport(uint256 _debtRepayment, uint256 _gain, uint256 _loss) external; /// @notice Emergency function to rescue tokens not related to the Multistrategy sent to the contract by mistake. /// @param token Address of the token that will be rescued. /// @param recipient Address of who will receive the tokens. function rescueToken(address token, address recipient) external; }
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity ^0.8.27; interface IStrategyAdapter { /// @notice Emitted when the slippage limit is set. /// @param slippageLimit The new slippage limit in basis points (BPS). event SlippageLimitSet(uint256 slippageLimit); /// @notice Returns the address of the multistrategy this Strategy belongs to. function multistrategy() external view returns (address); /// @notice Returns the address of the token used tby this strategy. /// @dev it should be the same as the token used by the multistrategy. function asset() external view returns (address); /// @notice Returns the identifier of this Strategy Adapter. function id() external view returns (string memory); /// @notice Returns the name of this Strategy Adapter. function name() external view returns (string memory); /// @notice Returns the current slippage limit in basis points (BPS). /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%. /// @return The maximum allowable slippage in basis points. function slippageLimit() external view returns (uint256); /// @notice Sets the maximum allowable slippage limit for withdrawals. /// @dev Slippage limit is expressed in basis points (BPS), where 10,000 BPS equals 100%. /// This limit represents the tolerated difference between the expected withdrawal amount /// and the actual amount withdrawn from the strategy. /// @param _slippageLimit The maximum allowable slippage in basis points. function setSlippageLimit(uint256 _slippageLimit) external; /// @notice Requests a credit to the multistrategy. The multistrategy will send the /// maximum amount of credit available for this strategy. function requestCredit() external; /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has /// made along an amount to be withdrawn and be used for debt repayment. /// @dev Only the owner can call it /// @param _amountToWithdraw Amount that will be withdrawn from the strategy and will /// be available for debt repayment. function sendReport(uint256 _amountToWithdraw) external; /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has made. /// @dev This report wont withdraw any funds to repay debt to the Multistrategy. /// Only the multistrategy can call it function askReport() external; /// @notice Sends a report to the Multistrategy after this strategy has been panicked. /// Reporting any gains or loss based on the balance the could be emergency withdrawn /// @dev This function should only be called after a strategy has been retired. function sendReportPanicked() external; /// @notice Withdraws `asset` from the strategy. /// @dev Only callable by the multistrategy. /// @param _amount Amount of tokens to withdraw from the strategy. function withdraw(uint256 _amount) external returns (uint256); /// @notice Returns the amount of `asset` this strategy holds. function totalAssets() external view returns (uint256); /// @notice Returns the gain or loss this strategy has made since the last report. /// @dev The returned values will account for max slippage. function currentPnL() external view returns (uint256, uint256); /// @notice Starts the panic process for this strategy. /// The panic process consists of: /// - Withdraw as much funds as possible from the underlying strategy. /// - Report back to the multistrategy with the available funds. /// - Revoke the allowance that this adapter has given to the underlying strategy. /// - Pauses this contract. function panic() external; /// @notice Pauses the smart contract. /// @dev Functions that implement the `paused` modifier will revert when called. /// Guardians and Owner can call this function function pause() external; /// @notice Unpauses the smart contract. /// @dev Functions that implement the `paused` won't revert when called. /// Only the Owner can call this function function unpause() external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.20; /// @title Errors /// @notice Library containing all custom errors the protocol may revert with. library Errors { /*////////////////////////////////////////////////////////////////////////// GENERICS //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when `msg.sender` is not the manager. error CallerNotManager(address caller); /// @notice Thrown when `msg.sender` is not a guardian. error CallerNotGuardian(address caller); /// @notice Thrown when `amount` is zero. error ZeroAmount(uint256 amount); /// @notice Thrown when setting an address to the zero address. error ZeroAddress(); /// @notice Thrown when `currentBalance` is lower than `amount`. error InsufficientBalance(uint256 currentBalance, uint256 amount); /// @notice Thrown when `addr` is an unexpected address. error InvalidAddress(address addr); /*////////////////////////////////////////////////////////////////////////// MULTISTRATEGY //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when performing an action on a non-active strategy. error StrategyNotActive(address strategy); /// @notice Thrown when performing an action on an active strategy. error StrategyAlreadyActive(address strategy); /// @notice Thrown when a strategy is reporting a gain and a loss simultaneously. error GainLossMismatch(); /// @notice Thrown when a deposit would exceed the depositLimit error DepositLimit(); /// @notice Thrown when the owner tries to set a fee above the maximum permitted fee. error ExcessiveFee(uint256 fee); /// @notice Thrown when the debtRatio of a strategy or a multistrategy is above 100%. error DebtRatioAboveMaximum(uint256 debtRatio); /// @notice Thrown when trying to remove a strategy from `withdrawOrder` that still has outstanding debt. error StrategyWithOutstandingDebt(); /// @notice Thrown when minDebtDelta is above maxDebtDelta or maxDebtDelta is below minDebtDelta. error InvalidDebtDelta(); /// @notice Thrown when a strategy is reporting a loss higher than its total debt. error InvalidStrategyLoss(); /// @notice Thrown when there is non-Zero Address following a Zero Address in withdrawOrder. error InvalidWithdrawOrder(); /// @notice Thrown when trying to add a new strategy to the multistrategy but it already reached the /// maximum amount of strategies. error MaximumAmountStrategies(); /// @notice Thrown when trying to remove a strategy that has a `debtRatio` greater than 0. error StrategyNotRetired(); /// @notice Thrown when there isn't enough liquidity to cover a withdraw /// @param assets The amount of assets requested. /// @param liquidity The current liquidity available in the contract. error InsufficientLiquidity(uint256 assets, uint256 liquidity); /// @notice Thrown when depositing / minting on a retired multistrategy. error Retired(); /*////////////////////////////////////////////////////////////////////////// STRATEGY ADAPTER //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when the caller is not the Multistrategy. error CallerNotMultistrategy(address caller); /// @notice Thrown when the `_asset` parameter on the constructor doesn't match /// the `deposit` token on Multistrategy. error AssetMismatch(address multAsset, address stratAsset); /// @notice Thrown when the requested slippage limit exceeds the maximum permitted value. /// @param slippageLimit The slippage limit in basis points (BPS). error SlippageLimitExceeded(uint256 slippageLimit); /// @notice Thrown when the actual slippage exceeds the allowed slippage. /// @param amount0 The expected amount after accounting for allowed slippage. /// @param amount1 The actual amount obtained. error SlippageCheckFailed(uint256 amount0, uint256 amount1); /// @notice Thrown when the reward added is the base asset of the Strategy. error InvalidRewardToken(address rewardToken); /*////////////////////////////////////////////////////////////////////////// ERC-4626 //////////////////////////////////////////////////////////////////////////*/ /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}. * * 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. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => 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 returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual 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 returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual 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 `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` 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 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); 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 `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` 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. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` 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. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20Metadata} from "../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]. */ 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 v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @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 v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) 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 FailedInnerCall(); } } }
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity 0.8.27; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol"; import { IMultistrategyAdminable } from "interfaces/infra/multistrategy/IMultistrategyAdminable.sol"; import { Errors } from "src/infra/libraries/Errors.sol"; abstract contract MultistrategyAdminable is IMultistrategyAdminable, Ownable, Pausable { /*////////////////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IMultistrategyAdminable address public manager; /// @inheritdoc IMultistrategyAdminable mapping(address guardianAddress => bool isActive) public guardians; /// @notice Sets the Owner and Manager addresses. /// @param _owner The address of the initial owner. /// @param _manager The address of the initial manager. constructor(address _owner, address _manager) Ownable(_owner) { manager = _manager; } /*////////////////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////////////////*/ /// @notice Reverts if called by any account other than the owner or the manager. modifier onlyManager() { require( msg.sender == owner() || msg.sender == manager, Errors.CallerNotManager(msg.sender) ); _; } /// @notice Reverts if called by any account other than the owner, the manager, or a guardian. modifier onlyGuardian() { require( msg.sender == owner() || msg.sender == manager || guardians[msg.sender], Errors.CallerNotGuardian(msg.sender) ); _; } /*////////////////////////////////////////////////////////////////////////// USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc IMultistrategyAdminable function setManager(address _manager) external onlyOwner { require(_manager != address(0), Errors.ZeroAddress()); manager = _manager; emit ManagerSet(_manager); } /// @inheritdoc IMultistrategyAdminable function enableGuardian(address _guardian) external onlyOwner { guardians[_guardian] = true; emit GuardianEnabled(_guardian); } /// @inheritdoc IMultistrategyAdminable function revokeGuardian(address _guardian) external onlyOwner { guardians[_guardian] = false; emit GuardianRevoked(_guardian); } /// @inheritdoc IMultistrategyAdminable function pause() external onlyGuardian { _pause(); } /// @inheritdoc IMultistrategyAdminable function unpause() external onlyOwner { _unpause(); } }
// SPDX-License-Identifier: GNU AGPLv3 pragma solidity ^0.8.27; import { IMultistrategyAdminable } from "interfaces/infra/multistrategy/IMultistrategyAdminable.sol"; import { MStrat } from "src/types/DataTypes.sol"; interface IMultistrategyManageable is IMultistrategyAdminable { /// @notice Emitted when the protocol fee recipient is set. /// @param _protocolFeeRecipient The address that will receive the protocol fee. event ProtocolFeeRecipientSet(address indexed _protocolFeeRecipient); /// @notice Emitted when the performance fee is set. /// @param _performanceFee The new performance fee value. event PerformanceFeeSet(uint256 _performanceFee); /// @notice Emitted when the deposit limit is set. /// @param _depositLimit The new deposit limit value. event DepositLimitSet(uint256 _depositLimit); /// @notice Emitted when the slippage limit is set. /// @param _slippageLimit The new slippage limit value. event SlippageLimitSet(uint256 _slippageLimit); /// @notice Emitted when a new withdrawal order has been set. event WithdrawOrderSet(); /// @notice Emitted when the debt ratio for a specific strategy is set. /// @param _strategy The address of the strategy whose debt ratio was updated. /// @param _debtRatio The new debt ratio value for the specified strategy. event StrategyDebtRatioSet(address indexed _strategy, uint256 _debtRatio); /// @notice Emitted when the minimum debt delta for a specific strategy is set. /// @param _strategy The address of the strategy whose minimum debt delta was updated. /// @param _minDebtDelta The new minimum debt delta value for the specified strategy. event StrategyMinDebtDeltaSet(address indexed _strategy, uint256 _minDebtDelta); /// @notice Emitted when the maximum debt delta for a specific strategy is set. /// @param _strategy The address of the strategy whose maximum debt delta was updated. /// @param _maxDebtDelta The new maximum debt delta value for the specified strategy. event StrategyMaxDebtDeltaSet(address indexed _strategy, uint256 _maxDebtDelta); /// @notice Emitted when a new strategy is added. /// @param _strategy The address of the newly added strategy. event StrategyAdded(address indexed _strategy); /// @notice Emitted when a strategy is retired. /// @param _strategy The address of the retired strategy. event StrategyRetired(address indexed _strategy); /// @notice Emitted when a strategy is removed. /// @param _strategy The address of the removed strategy. event StrategyRemoved(address indexed _strategy); /// @notice Emitted when the deposits into this multistrategy are paused. event MultistrategyRetired(); /// @notice Address that will receive performance fee. function protocolFeeRecipient() external view returns (address); /// @notice Fee on the yield generated (in BPS). /// @dev Performance fee is taken on `strategyReport()` function on the Multistrategy contract. function performanceFee() external view returns (uint256); /// @notice Limit for total assets the multistrategy can hold. function depositLimit() external view returns (uint256); /// @notice Debt ratio of the multistrategy across all strategies (in BPS). /// @dev The debt ratio cannot exceed 10_000 BPS (100 %). function debtRatio() external view returns (uint256); /// @notice Amount of tokens that the strategies have borrowed in total. function totalDebt() external view returns (uint256); /// @notice Returns the current slippage limit in basis points (BPS). /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%. function slippageLimit() external view returns (uint256); /// @notice Amount of active strategies. function activeStrategies() external view returns (uint8); /// @notice Returns true if multistrategy has been retired. function retired() external view returns (bool); /// @notice Returns the withdraw order. function getWithdrawOrder() external view returns (address[] memory); /// @notice Returns the strategy params of `strategy` /// @param _strategy Address of the strategy the it will returns the parameters of. function getStrategyParameters(address _strategy) external view returns (MStrat.StrategyParams calldata); /// @notice Sets the recipient address of the performance fee. /// @dev Emits a `SetProtocolFeeRecipient` event. /// @param _protocolFeeRecipient Address that will receive the fees. function setProtocolFeeRecipient(address _protocolFeeRecipient) external; /// @notice Sets the performance fee in BPS. /// @dev Reverts if `performanceFee` is above MAX_PERFORMANCE_FEE /// @dev Emits a `SetPerformanceFee` event. /// @param _performanceFee New performance fee. function setPerformanceFee(uint256 _performanceFee) external; /// @notice Sets the deposit limit. /// @dev Emits a `SetDepositLimit` event. /// @param _depositLimit New deposit limit. function setDepositLimit(uint256 _depositLimit) external; /// @notice Sets the slippage limit of this Multistrategy. /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%. /// @param _slippageLimit New slippage limit. function setSlippageLimit(uint256 _slippageLimit) external; /// @notice Sets the withdraw order. First position in the array will be the first strategy that it will get the funds withdrawn /// @dev It will revert if a strategy in the array is not active or if the array contains duplicate addresses. /// @param _strategies Array of strategy addresses function setWithdrawOrder(address[] memory _strategies) external; /// @notice Adds a strategy to the multistrategy. /// @dev The strategy will be appended to `withdrawOrder`. /// @param _strategy The address of the strategy. /// @param _debtRatio The share of total assets in the Multistrategy this strategy will have access to. /// @param _minDebtDelta Lower limit on the increase of debt. /// @param _maxDebtDelta Upper limit on the increase of debt. function addStrategy( address _strategy, uint256 _debtRatio, uint256 _minDebtDelta, uint256 _maxDebtDelta ) external; /// @notice Sets the strategy debtRatio to 0, which prevents any further deposits into the strategy. /// @dev Retiring a strategy will set the approval of `asset` to the retired strategy to 0. /// @param _strategy The address of the strategy that will be retired. function retireStrategy(address _strategy) external; /// @notice Removes a strategy from `withdrawOrder`. /// @param _strategy The address of the strategy that will be removed. function removeStrategy(address _strategy) external; /// @notice Change the debt ratio of a strategy. /// @param _strategy Address of the strategy. /// @param _debtRatio New debt ratio. function setStrategyDebtRatio(address _strategy, uint256 _debtRatio) external; /// @notice Change the minimum amount of debt a strategy can take. /// @dev Used to limit the minimum amount of debt a strategy should take. /// Taking a small credit wouldn't be optimal gas-wise. /// @param _strategy Address of the strategy. /// @param _minDebtDelta Lower limit of the change of debt. function setStrategyMinDebtDelta(address _strategy, uint256 _minDebtDelta) external; /// @notice Change the maximum amount of debt a strategy can take at once. /// @dev Used to protect large debt repayments or withdraws. Risks are IL, or low liquidity. /// @param _strategy Address of the strategy. /// @param _maxDebtDelta Upper limit of the change of debt. function setStrategyMaxDebtDelta(address _strategy, uint256 _maxDebtDelta) external; /// @notice Retires the Multistrategy. End of Life. function retire() external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.20; /// @notice Namespace for the structs used in `Multistrategy` library MStrat { /// @notice Struct that contains a strategy data /// @param activation Block.timestamp of when the strategy was activated. 0 means not active /// @param debtRatio Maximum amount the strategy can borrow from the Multistrategy (in BPS of total assets in a Multistrategy) /// @param lastReport /// @param minDebtDelta Lower limit on the increase or decrease of debt since last harvest /// @param maxDebtDelta Upper limit on the increase or decrease of debt since last harvest /// @param totalDebt Total debt that this strategy has /// @param totalGain Total gains that this strategy has realized /// @param totalLoss Total losses that this strategy has realized struct StrategyParams { uint256 activation; uint256 debtRatio; uint256 lastReport; uint256 minDebtDelta; uint256 maxDebtDelta; uint256 totalDebt; uint256 totalGain; uint256 totalLoss; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @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 (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {Context} from "../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 { bool private _paused; /** * @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); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @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 { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @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: GNU AGPLv3 pragma solidity ^0.8.27; /// @title IMultistrategyAdminable /// @notice Contract module that provides a 3 level access control mechanism, with an owner, a manager /// and a list of guardians that can be granted exclusive access to specific functions. /// The inheriting contract must set the initial owner and the initial manager in the constructor. interface IMultistrategyAdminable { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ /// @notice Emitted when a new manager is set. /// @param _manager The address of the new manager. event ManagerSet(address indexed _manager); /// @notice Emitted when a new guardian has been granted access. /// @param _guardian The address of the guardian. event GuardianEnabled(address indexed _guardian); /// @notice Emitted when a the access of a guardian has been revoked. /// @param _guardian The address of the guardian. event GuardianRevoked(address indexed _guardian); /*////////////////////////////////////////////////////////////////////////// CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice The address of the manager. function manager() external view returns (address); /// @notice List of addresses enabled as guardian. /// @param _guardian The address to check if it is a guardian. function guardians(address _guardian) external view returns (bool); /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Sets the manager address. /// @dev Doesn't revert if: /// - manager address is zero address. /// - manager address is the same as previous manager address. /// @param _manager Address of the new manager. function setManager(address _manager) external; /// @notice Enables an address to be a guardian. /// @dev Doesn't revert if: /// - guardian address is zero address. /// - guardian address is already enabled as guardian. /// @param _guardian The address of the guardian. function enableGuardian(address _guardian) external; /// @notice Revokes an address to be a guardian. /// @dev Doesn't revert if: /// - guardian address is zero address. /// - guardian address is already revoked. /// @param _guardian The address of the guardian. function revokeGuardian(address _guardian) external; /// @notice Pauses the smart contract. /// @dev Functions that implement the `paused` modifier will revert when called. /// Guardians, Manager and Owner can call this function function pause() external; /// @notice Unpauses the smart contract. /// @dev Functions that implement the `paused` won't revert when called. /// Guardians, Manager and Owner can call this function function unpause() external; }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "src/=src/", "interfaces/=src/interfaces/", "test-utils/=test/utils/", "createx/=lib/createx-forge/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/", "@openzeppelin-4/contracts/=lib/openzeppelin-contracts-4/contracts/", "@layerzero/=lib/solidity-examples/contracts/", "@aave/=lib/aave-v3-origin/src/", "@xerc20/=lib/xERC20/", "@uniswap/v3-periphery/=lib/v3-periphery/", "@uniswap/v3-core/=lib/v3-core/", "@addressbook/=address-book/", "@trust-security/trustlessPermit/=lib/trustlessPermit/", "@uniswapV3-periphery/=lib/v3-periphery/contracts/", "@prb/math/=lib/prb-math/", "@properties/=lib/properties/", "@prb/test/=lib/prb-math/node_modules/@prb/test/", "ERC4626/=lib/properties/lib/ERC4626/contracts/", "aave-v3-core/=lib/aave-v3-origin/src/core/", "aave-v3-origin/=lib/aave-v3-origin/", "aave-v3-periphery/=lib/aave-v3-origin/src/periphery/", "common/=lib/common/", "createx-forge/=lib/createx-forge/", "ds-test/=lib/solmate/lib/ds-test/src/", "erc4626-tests/=lib/erc4626-tests/", "openzeppelin-contracts-4/=lib/openzeppelin-contracts-4/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/", "openzeppelin/=lib/openzeppelin-contracts-4/contracts/", "prb-math/=lib/prb-math/src/", "properties/=lib/properties/contracts/", "solidity-examples/=lib/solidity-examples/contracts/", "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/", "solidity-utils/=lib/aave-v3-origin/lib/solidity-utils/", "solmate/=lib/solmate/src/", "trustlessPermit/=lib/trustlessPermit/", "v3-core/=lib/v3-core/", "v3-periphery/=lib/v3-periphery/contracts/", "xERC20/=lib/xERC20/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"address","name":"_protocolFeeRecipient","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"multAsset","type":"address"},{"internalType":"address","name":"stratAsset","type":"address"}],"name":"AssetMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotGuardian","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotManager","type":"error"},{"inputs":[{"internalType":"uint256","name":"debtRatio","type":"uint256"}],"name":"DebtRatioAboveMaximum","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxDeposit","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxMint","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxRedeem","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxWithdraw","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ExcessiveFee","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"GainLossMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentBalance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"InsufficientLiquidity","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidDebtDelta","type":"error"},{"inputs":[],"name":"InvalidStrategyLoss","type":"error"},{"inputs":[],"name":"InvalidWithdrawOrder","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"MaximumAmountStrategies","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"Retired","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"SlippageCheckFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"slippageLimit","type":"uint256"}],"name":"SlippageLimitExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyAlreadyActive","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyNotActive","type":"error"},{"inputs":[],"name":"StrategyNotRetired","type":"error"},{"inputs":[],"name":"StrategyWithOutstandingDebt","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreditRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"DepositLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_manager","type":"address"}],"name":"ManagerSet","type":"event"},{"anonymous":false,"inputs":[],"name":"MultistrategyRetired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_performanceFee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"ProtocolFeeRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_slippageLimit","type":"uint256"}],"name":"SlippageLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_debtRatio","type":"uint256"}],"name":"StrategyDebtRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"StrategyMaxDebtDeltaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_minDebtDelta","type":"uint256"}],"name":"StrategyMinDebtDeltaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"debtRepaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"}],"name":"StrategyReported","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"}],"name":"StrategyRetired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[],"name":"WithdrawOrderSet","type":"event"},{"inputs":[],"name":"DECIMALS_OFFSET","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEGRADATION_COEFFICIENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROFIT_UNLOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeStrategies","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_debtRatio","type":"uint256"},{"internalType":"uint256","name":"_minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"creditAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPnL","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"debtExcess","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"enableGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"getStrategyParameters","outputs":[{"components":[{"internalType":"uint256","name":"activation","type":"uint256"},{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"maxDebtDelta","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"totalGain","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"}],"internalType":"struct MStrat.StrategyParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawOrder","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardianAddress","type":"address"}],"name":"guardians","outputs":[{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfitDegradation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestCredit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"rescueToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"retireStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"revokeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_performanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"setProtocolFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slippageLimit","type":"uint256"}],"name":"setSlippageLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_debtRatio","type":"uint256"}],"name":"setStrategyDebtRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_maxDebtDelta","type":"uint256"}],"name":"setStrategyMaxDebtDelta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_minDebtDelta","type":"uint256"}],"name":"setStrategyMinDebtDelta","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"setWithdrawOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slippageLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategyAddress","type":"address"}],"name":"strategies","outputs":[{"internalType":"uint256","name":"activation","type":"uint256"},{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"minDebtDelta","type":"uint256"},{"internalType":"uint256","name":"maxDebtDelta","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"},{"internalType":"uint256","name":"totalGain","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtRepayment","type":"uint256"},{"internalType":"uint256","name":"_gain","type":"uint256"},{"internalType":"uint256","name":"_loss","type":"uint256"}],"name":"strategyReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"strategyTotalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"withdrawOrder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
610100604052348015610010575f5ffd5b506040516148bf3803806148bf83398101604081905261002f91610496565b8482823387878282818061005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100658161020a565b505f805460ff60a01b19169055600180546001600160a01b0319166001600160a01b03928316179055821690506100af5760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831617905560408051600a8082526101608201909252906020820161014080368337505081516100fc92600b925060200190610348565b5050505081600f908161010f91906105b0565b50601061011c82826105b0565b5050505f5f6101308361025960201b60201c565b9150915081610140576012610142565b805b60ff1660a05250506001600160a01b0390811660805260016011556040805163313ce56760e01b815290516101d7925f929089169163313ce567916004808201926020929091908290030181865afa1580156101a0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101c4919061066a565b6101d29060ff166012610691565b61032f565b60ff1660c0526103e8600455426012556101fc6203f480670de0b6b3a76400006106b0565b60e052506107019350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b0387169161029f916106cf565b5f60405180830381855afa9150503d805f81146102d7576040519150601f19603f3d011682016040523d82523d5f602084013e6102dc565b606091505b50915091508180156102f057506020815110155b15610323575f8180602001905181019061030a91906106ea565b905060ff8111610321576001969095509350505050565b505b505f9485945092505050565b5f81831161033d578161033f565b825b90505b92915050565b828054828255905f5260205f2090810192821561039b579160200282015b8281111561039b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190610366565b506103a79291506103ab565b5090565b5b808211156103a7575f81556001016103ac565b80516001600160a01b03811681146103d5575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f5b838110156104085781810151838201526020016103f0565b50505f910152565b5f82601f83011261041f575f5ffd5b81516001600160401b03811115610438576104386103da565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610466576104666103da565b60405281815283820160200185101561047d575f5ffd5b61048e8260208301602087016103ee565b949350505050565b5f5f5f5f5f60a086880312156104aa575f5ffd5b6104b3866103bf565b94506104c1602087016103bf565b93506104cf604087016103bf565b60608701519093506001600160401b038111156104ea575f5ffd5b6104f688828901610410565b608088015190935090506001600160401b03811115610513575f5ffd5b61051f88828901610410565b9150509295509295909350565b600181811c9082168061054057607f821691505b60208210810361055e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156105ab57805f5260205f20601f840160051c810160208510156105895750805b601f840160051c820191505b818110156105a8575f8155600101610595565b50505b505050565b81516001600160401b038111156105c9576105c96103da565b6105dd816105d7845461052c565b84610564565b6020601f82116001811461060f575f83156105f85750848201515b5f19600385901b1c1916600184901b1784556105a8565b5f84815260208120601f198516915b8281101561063e578785015182556020948501946001909201910161061e565b508482101561065b57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f6020828403121561067a575f5ffd5b815160ff8116811461068a575f5ffd5b9392505050565b8181038181111561034257634e487b7160e01b5f52601160045260245ffd5b5f826106ca57634e487b7160e01b5f52601260045260245ffd5b500490565b5f82516106e08184602087016103ee565b9190910192915050565b5f602082840312156106fa575f5ffd5b5051919050565b60805160a05160c05160e0516141376107885f395f8181610646015261386001525f81816108450152818161131201528181612314015261236601525f61133301525f8181610550015281816122710152818161262d0152818161291301528181612a9401528181612c8e01528181612ccb01528181613183015261326f01526141375ff3fe608060405234801561000f575f5ffd5b506004361061042d575f3560e01c8063877887821161022c578063c3535b5211610135578063dd62ed3e116100bf578063ef8b30f711610084578063ef8b30f714610907578063f10d3d5d146109fa578063f2fde38b14610a6d578063fb791b0b14610a80578063fc7b9c1814610a93575f5ffd5b8063dd62ed3e1461097e578063e2c23610146109b6578063e521cb92146109cb578063ec811718146109de578063ecf70858146109f1575f5ffd5b8063cea55f5711610105578063cea55f571461092d578063d0ebdbe714610936578063d250f08814610949578063d764801314610958578063d905777e1461096b575f5ffd5b8063c3535b52146108eb578063c63d75b6146108f4578063c6e6f59214610907578063ce96cb771461091a575f5ffd5b8063aea70acc116101b6578063b6b1fb3b11610186578063b6b1fb3b146108a0578063ba087652146108aa578063bdc8144b146108bd578063c031d2fb146108d0578063c285cbb5146108d8575f5ffd5b8063aea70acc14610840578063af648c3d14610867578063b3d7f6b91461087a578063b460af941461088d575f5ffd5b806399530b06116101fc57806399530b06146107f7578063a0c9d3fc146107ff578063a4874d7714610812578063a4c9b9611461081a578063a9059cbb1461082d575f5ffd5b806387788782146107c35780638da5cb5b146107cc57806394bf804d146107dc57806395d89b41146107ef575f5ffd5b8063422327161161033957806364df049e116102c3578063715018a611610288578063715018a61461078057806374f9479b146107885780637a9f35861461079b578063821a622f146107ae5780638456cb59146107bb575f5ffd5b806364df049e1461070c578063691083631461071f5780636e553f651461073257806370897b231461074557806370a0823114610758575f5ffd5b806349185ab81161030957806349185ab8146106975780634cdad506146106b45780635b07871a146106c75780635c975abb146106d057806361f488bf146106e1575f5ffd5b8063422327161461064157806344b81396146106685780634707d00014610671578063481c6a7514610684575f5ffd5b80631fe4ba17116103ba57806338d52e0f1161038a57806338d52e0f1461054e57806339ebf823146105885780633ea9e124146106135780633f4ba83a14610626578063402d267d1461062e575f5ffd5b80631fe4ba17146104fc57806323b872dd1461050f5780632eb38ae014610522578063313ce56714610534575f5ffd5b8063095ea7b311610400578063095ea7b3146104a65780630a28a477146104b95780630dd21b6c146104cc578063175188e8146104e157806318160ddd146104f4575f5ffd5b806301e1d114146104315780630633b14a1461044c57806306fdde031461047e57806307a2d13a14610493575b5f5ffd5b610439610a9c565b6040519081526020015b60405180910390f35b61046e61045a366004613af8565b60026020525f908152604090205460ff1681565b6040519015158152602001610443565b610486610ab7565b6040516104439190613b35565b6104396104a1366004613b67565b610b47565b61046e6104b4366004613b7e565b610b58565b6104396104c7366004613b67565b610b6f565b6104df6104da366004613ba8565b610bd3565b005b6104df6104ef366004613af8565b611022565b600e54610439565b6104df61050a366004613b67565b611237565b61046e61051d366004613be0565b6112e9565b60095461046e90610100900460ff1681565b61053c61130c565b60405160ff9091168152602001610443565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b039091168152602001610443565b6105d8610596366004613af8565b600a6020525f90815260409020805460018201546002830154600384015460048501546005860154600687015460079097015495969495939492939192909188565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e082015261010001610443565b6104df610621366004613b7e565b611357565b6104df611482565b61043961063c366004613af8565b611494565b6104397f000000000000000000000000000000000000000000000000000000000000000081565b61043960135481565b6104df61067f366004613c1e565b6114c6565b600154610570906001600160a01b031681565b61069f611537565b60408051928352602083019190915201610443565b6104396106c2366004613b67565b611549565b61043960085481565b5f54600160a01b900460ff1661046e565b6104396106ef366004613af8565b6001600160a01b03165f908152600a602052604090206005015490565b600354610570906001600160a01b031681565b6104df61072d366004613af8565b611589565b610439610740366004613c55565b611697565b6104df610753366004613b67565b61172c565b610439610766366004613af8565b6001600160a01b03165f908152600c602052604090205490565b6104df611791565b610439610796366004613af8565b6117a2565b6104df6107a9366004613c78565b6117ac565b60095461053c9060ff1681565b6104df611800565b61043960045481565b5f546001600160a01b0316610570565b6104396107ea366004613c55565b61186f565b6104866118f7565b610439611906565b6104df61080d366004613af8565b611918565b6104df61196b565b6104df610828366004613cb5565b611a0b565b61046e61083b366004613b7e565b611abe565b61053c7f000000000000000000000000000000000000000000000000000000000000000081565b6104df610875366004613af8565b611acb565b610439610888366004613b67565b611b1b565b61043961089b366004613d80565b611b27565b6104396203f48081565b6104396108b8366004613d80565b611bca565b6104df6108cb366004613b67565b611c5c565b610439611cdf565b6104df6108e6366004613b7e565b611d32565b61043960125481565b610439610902366004613af8565b611e50565b610439610915366004613b67565b611e5d565b610439610928366004613af8565b611e68565b61043960065481565b6104df610944366004613af8565b611e8a565b610439670de0b6b3a764000081565b610439610966366004613af8565b611f02565b610439610979366004613af8565b611f0c565b61043961098c366004613c1e565b6001600160a01b039182165f908152600d6020908152604080832093909416825291909152205490565b6109be611f29565b6040516104439190613dbf565b6104df6109d9366004613af8565b611f88565b6104df6109ec366004613b7e565b612000565b61043960055481565b610a0d610a08366004613af8565b612153565b60405161044391905f61010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6104df610a7b366004613af8565b612209565b610570610a8e366004613b67565b612246565b61043960075481565b5f600754610aa861226e565b610ab29190613e1e565b905090565b6060600f8054610ac690613e31565b80601f0160208091040260200160405190810160405280929190818152602001828054610af290613e31565b8015610b3d5780601f10610b1457610100808354040283529160200191610b3d565b820191905f5260205f20905b815481529060010190602001808311610b2057829003601f168201915b5050505050905090565b5f610b52825f6122f8565b92915050565b5f33610b65818585612350565b5060019392505050565b5f5f610b7c83600161235d565b9050610b8661226e565b8311610b925792915050565b61271060085403610ba657505f1992915050565b610bc6612710600854612710610bbc9190613e63565b83919060016123ac565b9392505050565b50919050565b610bdb6123fb565b600954600a60ff90911610610c035760405163819a681f60e01b815260040160405180910390fd5b6001600160a01b03841615801590610c2457506001600160a01b0384163014155b8490610c5457604051634726455360e11b81526001600160a01b0390911660048201526024015b60405180910390fd5b506001600160a01b0384165f908152600a6020526040902054849015610c995760405163060f34a560e51b81526001600160a01b039091166004820152602401610c4b565b50836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfa9190613e76565b6001600160a01b0316306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613e76565b6001600160a01b031614306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190613e76565b856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e09573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2d9190613e76565b9091610e5f57604051634e83a9b960e01b81526001600160a01b03928316600482015291166024820152604401610c4b565b505061271083600654610e729190613e1e565b111583600654610e829190613e1e565b90610ea357604051637698c0b760e11b8152600401610c4b91815260200190565b5080821115610ec5576040516328b7ed0d60e01b815260040160405180910390fd5b604080516101008101825242808252602080830187815283850192835260608401878152608085018781525f60a0870181815260c0880182815260e089018381526001600160a01b038f168452600a9097529882209751885593516001880155945160028701559051600386015551600485015551600584015592516006808401919091559251600790920191909155815485929190610f66908490613e1e565b90915550849050600b610f7b6001600a613e91565b60ff1681548110610f8e57610f8e613eaa565b5f918252602082200180546001600160a01b0319166001600160a01b03939093169290921790915560098054909190610fc99060ff16613ebe565b91906101000a81548160ff021916908360ff160217905550610fe9612427565b6040516001600160a01b038516907f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f1905f90a250505050565b5f546001600160a01b031633148061104457506001546001600160a01b031633145b339061106f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a6020526040902054819081906110b55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902060010154156110ef5760405163424ae5ab60e01b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040902060050154156111285760405163a617d47160e01b815260040160405180910390fd5b5f5b600a60ff8216101561123157826001600160a01b0316600b8260ff168154811061115657611156613eaa565b5f918252602090912001546001600160a01b031603611229575f600b8260ff168154811061118657611186613eaa565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152600a90915260408120819055600980549091906111d19060ff16613edc565b91906101000a81548160ff021916908360ff1602179055506111f1612427565b6040516001600160a01b038416907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea4905f90a2505050565b60010161112a565b505b5050565b5f546001600160a01b031633148061125957506001546001600160a01b031633145b339061128457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50806127108111156112ac57604051631920afa360e11b8152600401610c4b91815260200190565b5060088190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336112f6858285612518565b61130185858561257a565b506001949350505050565b5f610ab27f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613ef7565b5f546001600160a01b031633148061137957506001546001600160a01b031633145b33906113a457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906113ea5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060030154821015611426576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600401849055517fe6fd3fad502c8a253ce72fb45e7f218dde705007fa4d961e164e5117ff4e31f3906114759085815260200190565b60405180910390a2505050565b61148a6123fb565b6114926125d7565b565b5f6005546114a0610a9c565b106114ac57505f919050565b6114b4610a9c565b600554610b529190613e63565b919050565b5f546001600160a01b03163314806114e857506001546001600160a01b031633145b806115015750335f9081526002602052604090205460ff165b339061152c57604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611233828261262b565b5f5f61154161270b565b915091509091565b5f5f611555835f6122f8565b905061155f61226e565b811161156b5792915050565b610bc660085461271061157e9190613e63565b82906127105f6123ac565b5f546001600160a01b03163314806115ab57506001546001600160a01b031633145b33906115d657604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a60205260409020548190819061161c5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040812060010154600680549192909161164b908490613e63565b90915550506001600160a01b0382165f818152600a6020526040808220600101829055517f43328b6eb7f0f6711a295cfc22368af8227bf60b67a6f6a5454f6168726d3e259190a25050565b5f6116a0612847565b600954610100900460ff16156116c957604051630c15f68f60e41b815260040160405180910390fd5b6116d1612871565b5f6116db83611494565b90508284828082111561170457604051633c8097d960e11b8152600401610c4b93929190613f10565b5050505f61171185611e5d565b905061171f3385878461289b565b915050610b526001601155565b6117346123fb565b806107d081111561175b57604051633de00b8560e01b8152600401610c4b91815260200190565b5060048190556040518181527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d906020016112de565b6117996123fb565b6114925f6129aa565b5f610b52826129f9565b6117b4612847565b335f818152600a602052604090205481906117ee5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506117fa848484612a91565b50505050565b5f546001600160a01b031633148061182257506001546001600160a01b031633145b8061183b5750335f9081526002602052604090205460ff165b339061186657604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611492612d48565b5f611878612847565b600954610100900460ff16156118a157604051630c15f68f60e41b815260040160405180910390fd5b6118a9612871565b5f6118b383611e50565b9050828482808211156118dc5760405163284ff66760e01b8152600401610c4b93929190613f10565b5050505f6118e985611b1b565b905061171f3385838861289b565b606060108054610ac690613e31565b5f610ab2670de0b6b3a7640000610b47565b6119206123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19166001179055517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b5f546001600160a01b031633148061198d57506001546001600160a01b031633145b806119a65750335f9081526002602052604090205460ff165b33906119d157604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b506009805461ff0019166101001790556040517f762e62217e4ec85388fb7cba32a604823803326c60ed9f962a2b86512ca78d8c905f90a1565b5f546001600160a01b0316331480611a2d57506001546001600160a01b031633145b3390611a5857604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50611a6281612d8a565b611a7f5760405163660e947360e01b815260040160405180910390fd5b8051611a9290600b906020840190613a6b565b506040517f5c2915d8834f24b89a98a55fe6de71ad6dad79ef0b3052ff68231dbfcb011c88905f90a150565b5f33610b6581858561257a565b611ad36123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b5f610b528260016122f8565b5f611b30612847565b611b38612871565b5f611b4283611e68565b905082858280821115611b6b57604051633fa733bb60e21b8152600401610c4b93929190613f10565b5050505f611b7886610b6f565b90505f611b893387878a865f612eec565b91508290508181811115611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b50909350505050610bc66001601155565b5f611bd3612847565b611bdb612871565b5f611be583611f0c565b905082858280821115611c0e57604051632e52afbb60e21b8152600401610c4b93929190613f10565b5050505f611c1b86611549565b90505f611c2d338787858b6001612eec565b509050818181811015611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b5f546001600160a01b0316331480611c7e57506001546001600160a01b031633145b3390611ca957604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b5060058190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020016112de565b5f611ce8612847565b335f818152600a60205260409020548190611d225760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b50611d2b613216565b91505b5090565b5f546001600160a01b0316331480611d5457506001546001600160a01b031633145b3390611d7f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a602052604090205482908190611dc55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060040154821115611e01576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600301849055517fe0dccc38125c57629849ddb1eef292a3e47f8efdba994dc4efe9637c6b68010a906114759085815260200190565b5f610b5261091583611494565b5f610b52825f61235d565b6001600160a01b0381165f908152600c6020526040812054610b52905f6122f8565b611e926123fb565b6001600160a01b038116611eb95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a250565b5f610b52826132cd565b6001600160a01b0381165f908152600c6020526040812054610b52565b6060600b805480602002602001604051908101604052809291908181526020018280548015610b3d57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611f61575050505050905090565b611f906123fb565b6001600160a01b038116611fb75760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040517f5034c7ac62cd0031ddf2f1dcde2858132dd886d61032c9b2d4a99f0490e80ee4905f90a250565b5f546001600160a01b031633148061202257506001546001600160a01b031633145b339061204d57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906120935760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a602052604081206001015460065484916120bd91613e63565b6120c79190613e1e565b9050806127108111156120f057604051637698c0b760e11b8152600401610c4b91815260200190565b5060068190556001600160a01b0384165f818152600a602052604090819020600101859055517f64b9074a3c4edf892069f1a719f7b1de2f307df3b29626e0b381d819420419ef906121459086815260200190565b60405180910390a250505050565b6121936040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600a6020908152604091829020825161010081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e082015290565b6122116123fb565b6001600160a01b03811661223a57604051631e4fbdf760e01b81525f6004820152602401610c4b565b612243816129aa565b50565b600b8181548110612255575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f7f00000000000000000000000000000000000000000000000000000000000000006040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156122d4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab29190613f31565b5f610bc66123046133c0565b61230f906001613e1e565b61233a7f0000000000000000000000000000000000000000000000000000000000000000600a61402b565b600e546123479190613e1e565b859190856123ac565b61123183838360016133db565b5f610bc661238c7f0000000000000000000000000000000000000000000000000000000000000000600a61402b565b600e546123999190613e1e565b6123a16133c0565b612347906001613e1e565b5f5f6123b986868661349f565b90506123c48361355e565b80156123df57505f84806123da576123da614039565b868809115b156123f2576123ef600182613e1e565b90505b95945050505050565b5f546001600160a01b031633146114925760405163118cdaa760e01b8152336004820152602401610c4b565b5f805b600a60ff82161015611233575f600b8260ff168154811061244d5761244d613eaa565b5f918252602090912001546001600160a01b03169050806124785761247183613ebe565b925061250f565b60ff83161561250f5780600b61248e8585613e91565b60ff16815481106124a1576124a1613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505f600b8360ff16815481106124e3576124e3613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5060010161242a565b6001600160a01b038381165f908152600d60209081526040808320938616835292905220545f1981146117fa578181101561256c57828183604051637dc7a0d960e11b8152600401610c4b93929190613f10565b6117fa84848484035f6133db565b6001600160a01b0383166125a357604051634b637e8f60e11b81525f6004820152602401610c4b565b6001600160a01b0382166125cc5760405163ec442f0560e01b81525f6004820152602401610c4b565b61123183838361358a565b6125df61369d565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161415829061268c57604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b506040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156126d1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126f59190613f31565b90506112316001600160a01b03841683836136c6565b6009545f90819060ff16810361272357505f91829150565b5f80805b60095460ff908116908216101561283d575f600b8260ff168154811061274f5761274f613eaa565b5f918252602090912001546001600160a01b0316905080612770575061283d565b6001600160a01b0381165f908152600a602052604081206005015490036127975750612835565b5f5f826001600160a01b03166349185ab86040518163ffffffff1660e01b81526004016040805180830381865afa1580156127d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127f8919061404d565b9150915061281960045461271061280f9190613e63565b839061271061349f565b6128239087613e1e565b955061282f8186613e1e565b94505050505b600101612727565b5090939092509050565b5f54600160a01b900460ff16156114925760405163d93c066560e01b815260040160405180910390fd5b60026011540361289457604051633ee5aeb560e01b815260040160405180910390fd5b6002601155565b6001600160a01b038316158015906128bc57506001600160a01b0383163014155b83906128e757604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b50818061290a5760405163135ee08960e31b8152600401610c4b91815260200190565b506129448430847f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316929190613725565b61294e838261375e565b826001600160a01b0316846001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7848460405161299c929190918252602082015260400190565b60405180910390a350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f6006545f03612a2157506001600160a01b03165f908152600a602052604090206005015490565b5f612a51612a2d610a9c565b6001600160a01b0385165f908152600a60205260409020600101549061271061349f565b6001600160a01b0384165f908152600a6020526040902060050154909150818111612a7f57505f9392505050565b612a898282613e63565b949350505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006040516370a0823160e01b81523360048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612af7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b1b9190613f31565b90505f83118015612b2b57505f82115b15612b4957604051635b1ba80560e11b815260040160405180910390fd5b612b538385613e1e565b81101581612b618587613e1e565b9091612b895760405163cf47918160e01b815260048101929092526024820152604401610c4b565b505f9050808315612b9e57612b9e3385613792565b8415612bc457600454612bb590869061271061349f565b9050612bc18186613e63565b91505b5f612bd787612bd2336129f9565b613847565b90508015612c1e57335f908152600a602052604081206005018054839290612c00908490613e63565b925050819055508060075f828254612c189190613e63565b90915550505b5f83612c2861385c565b612c329190613e1e565b905085811115612c4e57612c468682613e63565b601355612c53565b5f6013555b335f908152600a60205260408120426002909101819055601255612c778884613e1e565b1115612cb257612cb23330612c8c8a86613e1e565b7f0000000000000000000000000000000000000000000000000000000000000000612933565b8215612cfb57600354612cfb906001600160a01b0316847f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031691906136c6565b604080518381526020810186905290810187905233907f6fbdf847c78f04eac7a7a77e1073af6eeec604ee0b495240579dccf57fcf2d889060600160405180910390a25050505050505050565b612d50612847565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861260e3390565b80515f90600a14612d9c57505f919050565b5f5b600a60ff82161015612ee3575f838260ff1681518110612dc057612dc0613eaa565b602002602001015190505f6001600160a01b0316816001600160a01b031614612e78576001600160a01b0381165f908152600a60205260408120549003612e0a57505f9392505050565b5f5b600a60ff82161015612e72578060ff168360ff1614158015612e5b5750848160ff1681518110612e3e57612e3e613eaa565b60200260200101516001600160a01b0316826001600160a01b0316145b15612e6a57505f949350505050565b600101612e0c565b50612eda565b5f612e84836001613ef7565b90505b600a60ff82161015611301575f6001600160a01b0316858260ff1681518110612eb257612eb2613eaa565b60200260200101516001600160a01b031614612ed257505f949350505050565b600101612e87565b50600101612d9e565b50600192915050565b5f808380612f105760405163135ee08960e31b8152600401610c4b91815260200190565b50856001600160a01b0316886001600160a01b031614612f3557612f35868986612518565b5f83612f415785612f4b565b612f4b855f6122f8565b9050612f5561226e565b811115613158575f5b600b5460ff821611613156575f600b8260ff1681548110612f8157612f81613eaa565b5f918252602090912001546001600160a01b0316905080151583612fa361226e565b9091612fcb5760405163a17e11d560e01b815260048101929092526024820152604401610c4b565b50505f613003612fd961226e565b612fe39086613e63565b6001600160a01b0384165f908152600a6020526040902060050154613847565b9050805f03613013575050613146565b604051632e1a7d4d60e01b8152600481018290525f906001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015613059573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061307d9190613f31565b6001600160a01b0384165f908152600a60205260408120600501805492935083929091906130ac908490613e63565b925050819055508060075f8282546130c49190613e63565b92505081905550826001600160a01b031663c0275d5b6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613103575f5ffd5b505af1158015613115573d5f5f3e3d5ffd5b50505050871561312c57613129895f6122f8565b94505b61313461226e565b851161314257505050613156565b5050505b61314f81613ebe565b9050612f5e565b505b5f8461316e5761316982600161235d565b613170565b855b905061317c88826138d4565b6131a789837f0000000000000000000000000000000000000000000000000000000000000000612ceb565b876001600160a01b0316896001600160a01b03168b6001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db85856040516131ff929190918252602082015260400190565b60405180910390a490999098509650505050505050565b5f5f613221336132cd565b905080156114c157335f908152600a60205260408120600501805483929061324a908490613e1e565b925050819055508060075f8282546132629190613e1e565b90915550613293905033827f0000000000000000000000000000000000000000000000000000000000000000612ceb565b60405181815233907f4bcdc8c4e44b31be744ac2494ab91b09234c21e24fa1cb0a1b4de3a85ef74d419060200160405180910390a2919050565b5f5f6132d7610a9c565b6006549091505f906132ec908361271061349f565b6007546001600160a01b0386165f908152600a6020526040812060010154929350909161331c908561271061349f565b6001600160a01b0387165f908152600a60205260409020600581015460038201546004909201549293509183831015806133565750858510155b1561336957505f98975050505050505050565b5f6133748486613e63565b90505f6133818789613e63565b905061338d8282613847565b9150838210156133a757505f9a9950505050505050505050565b6133b18284613847565b9b9a5050505050505050505050565b5f6133c961385c565b6133d1610a9c565b610ab29190613e63565b6001600160a01b0384166134045760405163e602df0560e01b81525f6004820152602401610c4b565b6001600160a01b03831661342d57604051634a1406b160e11b81525f6004820152602401610c4b565b6001600160a01b038085165f908152600d6020908152604080832093871683529290522082905580156117fa57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161299c91815260200190565b5f838302815f1985870982811083820303915050805f036134d3578382816134c9576134c9614039565b0492505050610bc6565b8084116134f35760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156135735761357361406f565b61357d9190614083565b60ff166001149050919050565b6001600160a01b0383166135b45780600e5f8282546135a99190613e1e565b909155506136119050565b6001600160a01b0383165f908152600c6020526040902054818110156135f35783818360405163391434e360e21b8152600401610c4b93929190613f10565b6001600160a01b0384165f908152600c602052604090209082900390555b6001600160a01b03821661362d57600e8054829003905561364b565b6001600160a01b0382165f908152600c602052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161369091815260200190565b60405180910390a3505050565b5f54600160a01b900460ff1661149257604051638dfc202b60e01b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261123191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613908565b6040516001600160a01b0384811660248301528381166044830152606482018390526117fa9186918216906323b872dd906084016136f3565b6001600160a01b0382166137875760405163ec442f0560e01b81525f6004820152602401610c4b565b6112335f838361358a565b6001600160a01b0382165f908152600a60205260409020600501548111156137cd576040516345f8381360e11b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040812060070180548392906137f7908490613e1e565b90915550506001600160a01b0382165f908152600a602052604081206005018054839290613826908490613e63565b925050819055508060075f82825461383e9190613e63565b90915550505050565b5f8183106138555781610bc6565b5090919050565b5f5f7f00000000000000000000000000000000000000000000000000000000000000006012544261388d9190613e63565b61389791906140b0565b9050670de0b6b3a76400008110156138cd576013546138c0908290670de0b6b3a764000061349f565b601354611d2b9190613e63565b5f91505090565b6001600160a01b0382166138fd57604051634b637e8f60e11b81525f6004820152602401610c4b565b611233825f8361358a565b5f61391c6001600160a01b03841683613969565b905080515f1415801561394057508080602001905181019061393e91906140c7565b155b1561123157604051635274afe760e01b81526001600160a01b0384166004820152602401610c4b565b6060610bc683835f845f5f856001600160a01b0316848660405161398d91906140e6565b5f6040518083038185875af1925050503d805f81146139c7576040519150601f19603f3d011682016040523d82523d5f602084013e6139cc565b606091505b50915091506139dc8683836139e6565b9695505050505050565b6060826139fb576139f682613a42565b610bc6565b8151158015613a1257506001600160a01b0384163b155b15613a3b57604051639996b31560e01b81526001600160a01b0385166004820152602401610c4b565b5080610bc6565b805115613a525780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b828054828255905f5260205f20908101928215613abe579160200282015b82811115613abe57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613a89565b50611d2e9291505b80821115611d2e575f8155600101613ac6565b6001600160a01b0381168114612243575f5ffd5b80356114c181613ad9565b5f60208284031215613b08575f5ffd5b8135610bc681613ad9565b5f5b83811015613b2d578181015183820152602001613b15565b50505f910152565b602081525f8251806020840152613b53816040850160208701613b13565b601f01601f19169190910160400192915050565b5f60208284031215613b77575f5ffd5b5035919050565b5f5f60408385031215613b8f575f5ffd5b8235613b9a81613ad9565b946020939093013593505050565b5f5f5f5f60808587031215613bbb575f5ffd5b8435613bc681613ad9565b966020860135965060408601359560600135945092505050565b5f5f5f60608486031215613bf2575f5ffd5b8335613bfd81613ad9565b92506020840135613c0d81613ad9565b929592945050506040919091013590565b5f5f60408385031215613c2f575f5ffd5b8235613c3a81613ad9565b91506020830135613c4a81613ad9565b809150509250929050565b5f5f60408385031215613c66575f5ffd5b823591506020830135613c4a81613ad9565b5f5f5f60608486031215613c8a575f5ffd5b505081359360208301359350604090920135919050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215613cc5575f5ffd5b813567ffffffffffffffff811115613cdb575f5ffd5b8201601f81018413613ceb575f5ffd5b803567ffffffffffffffff811115613d0557613d05613ca1565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d3257613d32613ca1565b604052918252602081840181019290810187841115613d4f575f5ffd5b6020850194505b83851015613d7557613d6785613aed565b815260209485019401613d56565b509695505050505050565b5f5f5f60608486031215613d92575f5ffd5b833592506020840135613da481613ad9565b91506040840135613db481613ad9565b809150509250925092565b602080825282518282018190525f918401906040840190835b81811015613dff5783516001600160a01b0316835260209384019390920191600101613dd8565b509095945050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b5257610b52613e0a565b600181811c90821680613e4557607f821691505b602082108103610bcd57634e487b7160e01b5f52602260045260245ffd5b81810381811115610b5257610b52613e0a565b5f60208284031215613e86575f5ffd5b8151610bc681613ad9565b60ff8281168282160390811115610b5257610b52613e0a565b634e487b7160e01b5f52603260045260245ffd5b5f60ff821660ff8103613ed357613ed3613e0a565b60010192915050565b5f60ff821680613eee57613eee613e0a565b5f190192915050565b60ff8181168382160190811115610b5257610b52613e0a565b6001600160a01b039390931683526020830191909152604082015260600190565b5f60208284031215613f41575f5ffd5b5051919050565b6001815b6001841115613f8357808504811115613f6757613f67613e0a565b6001841615613f7557908102905b60019390931c928002613f4c565b935093915050565b5f82613f9957506001610b52565b81613fa557505f610b52565b8160018114613fbb5760028114613fc557613fe1565b6001915050610b52565b60ff841115613fd657613fd6613e0a565b50506001821b610b52565b5060208310610133831016604e8410600b8410161715614004575081810a610b52565b6140105f198484613f48565b805f190482111561402357614023613e0a565b029392505050565b5f610bc660ff841683613f8b565b634e487b7160e01b5f52601260045260245ffd5b5f5f6040838503121561405e575f5ffd5b505080516020909101519092909150565b634e487b7160e01b5f52602160045260245ffd5b5f60ff8316806140a157634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b8082028115828204841417610b5257610b52613e0a565b5f602082840312156140d7575f5ffd5b81518015158114610bc6575f5ffd5b5f82516140f7818460208701613b13565b919091019291505056fea26469706673582212204e695dd3a83dc12d398f9fcb85a9480fab4d5e83b9cbe84928b15bbb0fdc9c1664736f6c634300081b003300000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889400000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000135969656c642043686173696e672055534443650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077963555344436500000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f5ffd5b506004361061042d575f3560e01c8063877887821161022c578063c3535b5211610135578063dd62ed3e116100bf578063ef8b30f711610084578063ef8b30f714610907578063f10d3d5d146109fa578063f2fde38b14610a6d578063fb791b0b14610a80578063fc7b9c1814610a93575f5ffd5b8063dd62ed3e1461097e578063e2c23610146109b6578063e521cb92146109cb578063ec811718146109de578063ecf70858146109f1575f5ffd5b8063cea55f5711610105578063cea55f571461092d578063d0ebdbe714610936578063d250f08814610949578063d764801314610958578063d905777e1461096b575f5ffd5b8063c3535b52146108eb578063c63d75b6146108f4578063c6e6f59214610907578063ce96cb771461091a575f5ffd5b8063aea70acc116101b6578063b6b1fb3b11610186578063b6b1fb3b146108a0578063ba087652146108aa578063bdc8144b146108bd578063c031d2fb146108d0578063c285cbb5146108d8575f5ffd5b8063aea70acc14610840578063af648c3d14610867578063b3d7f6b91461087a578063b460af941461088d575f5ffd5b806399530b06116101fc57806399530b06146107f7578063a0c9d3fc146107ff578063a4874d7714610812578063a4c9b9611461081a578063a9059cbb1461082d575f5ffd5b806387788782146107c35780638da5cb5b146107cc57806394bf804d146107dc57806395d89b41146107ef575f5ffd5b8063422327161161033957806364df049e116102c3578063715018a611610288578063715018a61461078057806374f9479b146107885780637a9f35861461079b578063821a622f146107ae5780638456cb59146107bb575f5ffd5b806364df049e1461070c578063691083631461071f5780636e553f651461073257806370897b231461074557806370a0823114610758575f5ffd5b806349185ab81161030957806349185ab8146106975780634cdad506146106b45780635b07871a146106c75780635c975abb146106d057806361f488bf146106e1575f5ffd5b8063422327161461064157806344b81396146106685780634707d00014610671578063481c6a7514610684575f5ffd5b80631fe4ba17116103ba57806338d52e0f1161038a57806338d52e0f1461054e57806339ebf823146105885780633ea9e124146106135780633f4ba83a14610626578063402d267d1461062e575f5ffd5b80631fe4ba17146104fc57806323b872dd1461050f5780632eb38ae014610522578063313ce56714610534575f5ffd5b8063095ea7b311610400578063095ea7b3146104a65780630a28a477146104b95780630dd21b6c146104cc578063175188e8146104e157806318160ddd146104f4575f5ffd5b806301e1d114146104315780630633b14a1461044c57806306fdde031461047e57806307a2d13a14610493575b5f5ffd5b610439610a9c565b6040519081526020015b60405180910390f35b61046e61045a366004613af8565b60026020525f908152604090205460ff1681565b6040519015158152602001610443565b610486610ab7565b6040516104439190613b35565b6104396104a1366004613b67565b610b47565b61046e6104b4366004613b7e565b610b58565b6104396104c7366004613b67565b610b6f565b6104df6104da366004613ba8565b610bd3565b005b6104df6104ef366004613af8565b611022565b600e54610439565b6104df61050a366004613b67565b611237565b61046e61051d366004613be0565b6112e9565b60095461046e90610100900460ff1681565b61053c61130c565b60405160ff9091168152602001610443565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6040516001600160a01b039091168152602001610443565b6105d8610596366004613af8565b600a6020525f90815260409020805460018201546002830154600384015460048501546005860154600687015460079097015495969495939492939192909188565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e082015261010001610443565b6104df610621366004613b7e565b611357565b6104df611482565b61043961063c366004613af8565b611494565b6104397f000000000000000000000000000000000000000000000000000003824430f69e81565b61043960135481565b6104df61067f366004613c1e565b6114c6565b600154610570906001600160a01b031681565b61069f611537565b60408051928352602083019190915201610443565b6104396106c2366004613b67565b611549565b61043960085481565b5f54600160a01b900460ff1661046e565b6104396106ef366004613af8565b6001600160a01b03165f908152600a602052604090206005015490565b600354610570906001600160a01b031681565b6104df61072d366004613af8565b611589565b610439610740366004613c55565b611697565b6104df610753366004613b67565b61172c565b610439610766366004613af8565b6001600160a01b03165f908152600c602052604090205490565b6104df611791565b610439610796366004613af8565b6117a2565b6104df6107a9366004613c78565b6117ac565b60095461053c9060ff1681565b6104df611800565b61043960045481565b5f546001600160a01b0316610570565b6104396107ea366004613c55565b61186f565b6104866118f7565b610439611906565b6104df61080d366004613af8565b611918565b6104df61196b565b6104df610828366004613cb5565b611a0b565b61046e61083b366004613b7e565b611abe565b61053c7f000000000000000000000000000000000000000000000000000000000000000c81565b6104df610875366004613af8565b611acb565b610439610888366004613b67565b611b1b565b61043961089b366004613d80565b611b27565b6104396203f48081565b6104396108b8366004613d80565b611bca565b6104df6108cb366004613b67565b611c5c565b610439611cdf565b6104df6108e6366004613b7e565b611d32565b61043960125481565b610439610902366004613af8565b611e50565b610439610915366004613b67565b611e5d565b610439610928366004613af8565b611e68565b61043960065481565b6104df610944366004613af8565b611e8a565b610439670de0b6b3a764000081565b610439610966366004613af8565b611f02565b610439610979366004613af8565b611f0c565b61043961098c366004613c1e565b6001600160a01b039182165f908152600d6020908152604080832093909416825291909152205490565b6109be611f29565b6040516104439190613dbf565b6104df6109d9366004613af8565b611f88565b6104df6109ec366004613b7e565b612000565b61043960055481565b610a0d610a08366004613af8565b612153565b60405161044391905f61010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6104df610a7b366004613af8565b612209565b610570610a8e366004613b67565b612246565b61043960075481565b5f600754610aa861226e565b610ab29190613e1e565b905090565b6060600f8054610ac690613e31565b80601f0160208091040260200160405190810160405280929190818152602001828054610af290613e31565b8015610b3d5780601f10610b1457610100808354040283529160200191610b3d565b820191905f5260205f20905b815481529060010190602001808311610b2057829003601f168201915b5050505050905090565b5f610b52825f6122f8565b92915050565b5f33610b65818585612350565b5060019392505050565b5f5f610b7c83600161235d565b9050610b8661226e565b8311610b925792915050565b61271060085403610ba657505f1992915050565b610bc6612710600854612710610bbc9190613e63565b83919060016123ac565b9392505050565b50919050565b610bdb6123fb565b600954600a60ff90911610610c035760405163819a681f60e01b815260040160405180910390fd5b6001600160a01b03841615801590610c2457506001600160a01b0384163014155b8490610c5457604051634726455360e11b81526001600160a01b0390911660048201526024015b60405180910390fd5b506001600160a01b0384165f908152600a6020526040902054849015610c995760405163060f34a560e51b81526001600160a01b039091166004820152602401610c4b565b50836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfa9190613e76565b6001600160a01b0316306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613e76565b6001600160a01b031614306001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190613e76565b856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e09573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2d9190613e76565b9091610e5f57604051634e83a9b960e01b81526001600160a01b03928316600482015291166024820152604401610c4b565b505061271083600654610e729190613e1e565b111583600654610e829190613e1e565b90610ea357604051637698c0b760e11b8152600401610c4b91815260200190565b5080821115610ec5576040516328b7ed0d60e01b815260040160405180910390fd5b604080516101008101825242808252602080830187815283850192835260608401878152608085018781525f60a0870181815260c0880182815260e089018381526001600160a01b038f168452600a9097529882209751885593516001880155945160028701559051600386015551600485015551600584015592516006808401919091559251600790920191909155815485929190610f66908490613e1e565b90915550849050600b610f7b6001600a613e91565b60ff1681548110610f8e57610f8e613eaa565b5f918252602082200180546001600160a01b0319166001600160a01b03939093169290921790915560098054909190610fc99060ff16613ebe565b91906101000a81548160ff021916908360ff160217905550610fe9612427565b6040516001600160a01b038516907f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f1905f90a250505050565b5f546001600160a01b031633148061104457506001546001600160a01b031633145b339061106f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a6020526040902054819081906110b55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902060010154156110ef5760405163424ae5ab60e01b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040902060050154156111285760405163a617d47160e01b815260040160405180910390fd5b5f5b600a60ff8216101561123157826001600160a01b0316600b8260ff168154811061115657611156613eaa565b5f918252602090912001546001600160a01b031603611229575f600b8260ff168154811061118657611186613eaa565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152600a90915260408120819055600980549091906111d19060ff16613edc565b91906101000a81548160ff021916908360ff1602179055506111f1612427565b6040516001600160a01b038416907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea4905f90a2505050565b60010161112a565b505b5050565b5f546001600160a01b031633148061125957506001546001600160a01b031633145b339061128457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50806127108111156112ac57604051631920afa360e11b8152600401610c4b91815260200190565b5060088190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336112f6858285612518565b61130185858561257a565b506001949350505050565b5f610ab27f000000000000000000000000000000000000000000000000000000000000000c7f0000000000000000000000000000000000000000000000000000000000000006613ef7565b5f546001600160a01b031633148061137957506001546001600160a01b031633145b33906113a457604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906113ea5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060030154821015611426576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600401849055517fe6fd3fad502c8a253ce72fb45e7f218dde705007fa4d961e164e5117ff4e31f3906114759085815260200190565b60405180910390a2505050565b61148a6123fb565b6114926125d7565b565b5f6005546114a0610a9c565b106114ac57505f919050565b6114b4610a9c565b600554610b529190613e63565b919050565b5f546001600160a01b03163314806114e857506001546001600160a01b031633145b806115015750335f9081526002602052604090205460ff165b339061152c57604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611233828261262b565b5f5f61154161270b565b915091509091565b5f5f611555835f6122f8565b905061155f61226e565b811161156b5792915050565b610bc660085461271061157e9190613e63565b82906127105f6123ac565b5f546001600160a01b03163314806115ab57506001546001600160a01b031633145b33906115d657604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0381165f908152600a60205260409020548190819061161c5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040812060010154600680549192909161164b908490613e63565b90915550506001600160a01b0382165f818152600a6020526040808220600101829055517f43328b6eb7f0f6711a295cfc22368af8227bf60b67a6f6a5454f6168726d3e259190a25050565b5f6116a0612847565b600954610100900460ff16156116c957604051630c15f68f60e41b815260040160405180910390fd5b6116d1612871565b5f6116db83611494565b90508284828082111561170457604051633c8097d960e11b8152600401610c4b93929190613f10565b5050505f61171185611e5d565b905061171f3385878461289b565b915050610b526001601155565b6117346123fb565b806107d081111561175b57604051633de00b8560e01b8152600401610c4b91815260200190565b5060048190556040518181527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d906020016112de565b6117996123fb565b6114925f6129aa565b5f610b52826129f9565b6117b4612847565b335f818152600a602052604090205481906117ee5760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506117fa848484612a91565b50505050565b5f546001600160a01b031633148061182257506001546001600160a01b031633145b8061183b5750335f9081526002602052604090205460ff165b339061186657604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b50611492612d48565b5f611878612847565b600954610100900460ff16156118a157604051630c15f68f60e41b815260040160405180910390fd5b6118a9612871565b5f6118b383611e50565b9050828482808211156118dc5760405163284ff66760e01b8152600401610c4b93929190613f10565b5050505f6118e985611b1b565b905061171f3385838861289b565b606060108054610ac690613e31565b5f610ab2670de0b6b3a7640000610b47565b6119206123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19166001179055517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b5f546001600160a01b031633148061198d57506001546001600160a01b031633145b806119a65750335f9081526002602052604090205460ff165b33906119d157604051631f851be960e31b81526001600160a01b039091166004820152602401610c4b565b506009805461ff0019166101001790556040517f762e62217e4ec85388fb7cba32a604823803326c60ed9f962a2b86512ca78d8c905f90a1565b5f546001600160a01b0316331480611a2d57506001546001600160a01b031633145b3390611a5857604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b50611a6281612d8a565b611a7f5760405163660e947360e01b815260040160405180910390fd5b8051611a9290600b906020840190613a6b565b506040517f5c2915d8834f24b89a98a55fe6de71ad6dad79ef0b3052ff68231dbfcb011c88905f90a150565b5f33610b6581858561257a565b611ad36123fb565b6001600160a01b0381165f81815260026020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b5f610b528260016122f8565b5f611b30612847565b611b38612871565b5f611b4283611e68565b905082858280821115611b6b57604051633fa733bb60e21b8152600401610c4b93929190613f10565b5050505f611b7886610b6f565b90505f611b893387878a865f612eec565b91508290508181811115611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b50909350505050610bc66001601155565b5f611bd3612847565b611bdb612871565b5f611be583611f0c565b905082858280821115611c0e57604051632e52afbb60e21b8152600401610c4b93929190613f10565b5050505f611c1b86611549565b90505f611c2d338787858b6001612eec565b509050818181811015611bb957604051630a8f6c8d60e41b815260048101929092526024820152604401610c4b565b5f546001600160a01b0316331480611c7e57506001546001600160a01b031633145b3390611ca957604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b5060058190556040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f2906020016112de565b5f611ce8612847565b335f818152600a60205260409020548190611d225760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b50611d2b613216565b91505b5090565b5f546001600160a01b0316331480611d5457506001546001600160a01b031633145b3390611d7f57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a602052604090205482908190611dc55760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a6020526040902060040154821115611e01576040516328b7ed0d60e01b815260040160405180910390fd5b6001600160a01b0383165f818152600a602052604090819020600301849055517fe0dccc38125c57629849ddb1eef292a3e47f8efdba994dc4efe9637c6b68010a906114759085815260200190565b5f610b5261091583611494565b5f610b52825f61235d565b6001600160a01b0381165f908152600c6020526040812054610b52905f6122f8565b611e926123fb565b6001600160a01b038116611eb95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a250565b5f610b52826132cd565b6001600160a01b0381165f908152600c6020526040812054610b52565b6060600b805480602002602001604051908101604052809291908181526020018280548015610b3d57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611f61575050505050905090565b611f906123fb565b6001600160a01b038116611fb75760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040517f5034c7ac62cd0031ddf2f1dcde2858132dd886d61032c9b2d4a99f0490e80ee4905f90a250565b5f546001600160a01b031633148061202257506001546001600160a01b031633145b339061204d57604051633b2495f160e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0382165f908152600a6020526040902054829081906120935760405163e5c0758760e01b81526001600160a01b039091166004820152602401610c4b565b506001600160a01b0383165f908152600a602052604081206001015460065484916120bd91613e63565b6120c79190613e1e565b9050806127108111156120f057604051637698c0b760e11b8152600401610c4b91815260200190565b5060068190556001600160a01b0384165f818152600a602052604090819020600101859055517f64b9074a3c4edf892069f1a719f7b1de2f307df3b29626e0b381d819420419ef906121459086815260200190565b60405180910390a250505050565b6121936040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600a6020908152604091829020825161010081018452815481526001820154928101929092526002810154928201929092526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e082015290565b6122116123fb565b6001600160a01b03811661223a57604051631e4fbdf760e01b81525f6004820152602401610c4b565b612243816129aa565b50565b600b8181548110612255575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156122d4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab29190613f31565b5f610bc66123046133c0565b61230f906001613e1e565b61233a7f000000000000000000000000000000000000000000000000000000000000000c600a61402b565b600e546123479190613e1e565b859190856123ac565b61123183838360016133db565b5f610bc661238c7f000000000000000000000000000000000000000000000000000000000000000c600a61402b565b600e546123999190613e1e565b6123a16133c0565b612347906001613e1e565b5f5f6123b986868661349f565b90506123c48361355e565b80156123df57505f84806123da576123da614039565b868809115b156123f2576123ef600182613e1e565b90505b95945050505050565b5f546001600160a01b031633146114925760405163118cdaa760e01b8152336004820152602401610c4b565b5f805b600a60ff82161015611233575f600b8260ff168154811061244d5761244d613eaa565b5f918252602090912001546001600160a01b03169050806124785761247183613ebe565b925061250f565b60ff83161561250f5780600b61248e8585613e91565b60ff16815481106124a1576124a1613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505f600b8360ff16815481106124e3576124e3613eaa565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5060010161242a565b6001600160a01b038381165f908152600d60209081526040808320938616835292905220545f1981146117fa578181101561256c57828183604051637dc7a0d960e11b8152600401610c4b93929190613f10565b6117fa84848484035f6133db565b6001600160a01b0383166125a357604051634b637e8f60e11b81525f6004820152602401610c4b565b6001600160a01b0382166125cc5760405163ec442f0560e01b81525f6004820152602401610c4b565b61123183838361358a565b6125df61369d565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316826001600160a01b03161415829061268c57604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b506040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156126d1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126f59190613f31565b90506112316001600160a01b03841683836136c6565b6009545f90819060ff16810361272357505f91829150565b5f80805b60095460ff908116908216101561283d575f600b8260ff168154811061274f5761274f613eaa565b5f918252602090912001546001600160a01b0316905080612770575061283d565b6001600160a01b0381165f908152600a602052604081206005015490036127975750612835565b5f5f826001600160a01b03166349185ab86040518163ffffffff1660e01b81526004016040805180830381865afa1580156127d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127f8919061404d565b9150915061281960045461271061280f9190613e63565b839061271061349f565b6128239087613e1e565b955061282f8186613e1e565b94505050505b600101612727565b5090939092509050565b5f54600160a01b900460ff16156114925760405163d93c066560e01b815260040160405180910390fd5b60026011540361289457604051633ee5aeb560e01b815260040160405180910390fd5b6002601155565b6001600160a01b038316158015906128bc57506001600160a01b0383163014155b83906128e757604051634726455360e11b81526001600160a01b039091166004820152602401610c4b565b50818061290a5760405163135ee08960e31b8152600401610c4b91815260200190565b506129448430847f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6001600160a01b0316929190613725565b61294e838261375e565b826001600160a01b0316846001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7848460405161299c929190918252602082015260400190565b60405180910390a350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f6006545f03612a2157506001600160a01b03165f908152600a602052604090206005015490565b5f612a51612a2d610a9c565b6001600160a01b0385165f908152600a60205260409020600101549061271061349f565b6001600160a01b0384165f908152600a6020526040902060050154909150818111612a7f57505f9392505050565b612a898282613e63565b949350505050565b5f7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946040516370a0823160e01b81523360048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612af7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b1b9190613f31565b90505f83118015612b2b57505f82115b15612b4957604051635b1ba80560e11b815260040160405180910390fd5b612b538385613e1e565b81101581612b618587613e1e565b9091612b895760405163cf47918160e01b815260048101929092526024820152604401610c4b565b505f9050808315612b9e57612b9e3385613792565b8415612bc457600454612bb590869061271061349f565b9050612bc18186613e63565b91505b5f612bd787612bd2336129f9565b613847565b90508015612c1e57335f908152600a602052604081206005018054839290612c00908490613e63565b925050819055508060075f828254612c189190613e63565b90915550505b5f83612c2861385c565b612c329190613e1e565b905085811115612c4e57612c468682613e63565b601355612c53565b5f6013555b335f908152600a60205260408120426002909101819055601255612c778884613e1e565b1115612cb257612cb23330612c8c8a86613e1e565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612933565b8215612cfb57600354612cfb906001600160a01b0316847f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388945b6001600160a01b031691906136c6565b604080518381526020810186905290810187905233907f6fbdf847c78f04eac7a7a77e1073af6eeec604ee0b495240579dccf57fcf2d889060600160405180910390a25050505050505050565b612d50612847565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861260e3390565b80515f90600a14612d9c57505f919050565b5f5b600a60ff82161015612ee3575f838260ff1681518110612dc057612dc0613eaa565b602002602001015190505f6001600160a01b0316816001600160a01b031614612e78576001600160a01b0381165f908152600a60205260408120549003612e0a57505f9392505050565b5f5b600a60ff82161015612e72578060ff168360ff1614158015612e5b5750848160ff1681518110612e3e57612e3e613eaa565b60200260200101516001600160a01b0316826001600160a01b0316145b15612e6a57505f949350505050565b600101612e0c565b50612eda565b5f612e84836001613ef7565b90505b600a60ff82161015611301575f6001600160a01b0316858260ff1681518110612eb257612eb2613eaa565b60200260200101516001600160a01b031614612ed257505f949350505050565b600101612e87565b50600101612d9e565b50600192915050565b5f808380612f105760405163135ee08960e31b8152600401610c4b91815260200190565b50856001600160a01b0316886001600160a01b031614612f3557612f35868986612518565b5f83612f415785612f4b565b612f4b855f6122f8565b9050612f5561226e565b811115613158575f5b600b5460ff821611613156575f600b8260ff1681548110612f8157612f81613eaa565b5f918252602090912001546001600160a01b0316905080151583612fa361226e565b9091612fcb5760405163a17e11d560e01b815260048101929092526024820152604401610c4b565b50505f613003612fd961226e565b612fe39086613e63565b6001600160a01b0384165f908152600a6020526040902060050154613847565b9050805f03613013575050613146565b604051632e1a7d4d60e01b8152600481018290525f906001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015613059573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061307d9190613f31565b6001600160a01b0384165f908152600a60205260408120600501805492935083929091906130ac908490613e63565b925050819055508060075f8282546130c49190613e63565b92505081905550826001600160a01b031663c0275d5b6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015613103575f5ffd5b505af1158015613115573d5f5f3e3d5ffd5b50505050871561312c57613129895f6122f8565b94505b61313461226e565b851161314257505050613156565b5050505b61314f81613ebe565b9050612f5e565b505b5f8461316e5761316982600161235d565b613170565b855b905061317c88826138d4565b6131a789837f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612ceb565b876001600160a01b0316896001600160a01b03168b6001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db85856040516131ff929190918252602082015260400190565b60405180910390a490999098509650505050505050565b5f5f613221336132cd565b905080156114c157335f908152600a60205260408120600501805483929061324a908490613e1e565b925050819055508060075f8282546132629190613e1e565b90915550613293905033827f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612ceb565b60405181815233907f4bcdc8c4e44b31be744ac2494ab91b09234c21e24fa1cb0a1b4de3a85ef74d419060200160405180910390a2919050565b5f5f6132d7610a9c565b6006549091505f906132ec908361271061349f565b6007546001600160a01b0386165f908152600a6020526040812060010154929350909161331c908561271061349f565b6001600160a01b0387165f908152600a60205260409020600581015460038201546004909201549293509183831015806133565750858510155b1561336957505f98975050505050505050565b5f6133748486613e63565b90505f6133818789613e63565b905061338d8282613847565b9150838210156133a757505f9a9950505050505050505050565b6133b18284613847565b9b9a5050505050505050505050565b5f6133c961385c565b6133d1610a9c565b610ab29190613e63565b6001600160a01b0384166134045760405163e602df0560e01b81525f6004820152602401610c4b565b6001600160a01b03831661342d57604051634a1406b160e11b81525f6004820152602401610c4b565b6001600160a01b038085165f908152600d6020908152604080832093871683529290522082905580156117fa57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161299c91815260200190565b5f838302815f1985870982811083820303915050805f036134d3578382816134c9576134c9614039565b0492505050610bc6565b8084116134f35760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60028260038111156135735761357361406f565b61357d9190614083565b60ff166001149050919050565b6001600160a01b0383166135b45780600e5f8282546135a99190613e1e565b909155506136119050565b6001600160a01b0383165f908152600c6020526040902054818110156135f35783818360405163391434e360e21b8152600401610c4b93929190613f10565b6001600160a01b0384165f908152600c602052604090209082900390555b6001600160a01b03821661362d57600e8054829003905561364b565b6001600160a01b0382165f908152600c602052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161369091815260200190565b60405180910390a3505050565b5f54600160a01b900460ff1661149257604051638dfc202b60e01b815260040160405180910390fd5b6040516001600160a01b0383811660248301526044820183905261123191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613908565b6040516001600160a01b0384811660248301528381166044830152606482018390526117fa9186918216906323b872dd906084016136f3565b6001600160a01b0382166137875760405163ec442f0560e01b81525f6004820152602401610c4b565b6112335f838361358a565b6001600160a01b0382165f908152600a60205260409020600501548111156137cd576040516345f8381360e11b815260040160405180910390fd5b6001600160a01b0382165f908152600a6020526040812060070180548392906137f7908490613e1e565b90915550506001600160a01b0382165f908152600a602052604081206005018054839290613826908490613e63565b925050819055508060075f82825461383e9190613e63565b90915550505050565b5f8183106138555781610bc6565b5090919050565b5f5f7f000000000000000000000000000000000000000000000000000003824430f69e6012544261388d9190613e63565b61389791906140b0565b9050670de0b6b3a76400008110156138cd576013546138c0908290670de0b6b3a764000061349f565b601354611d2b9190613e63565b5f91505090565b6001600160a01b0382166138fd57604051634b637e8f60e11b81525f6004820152602401610c4b565b611233825f8361358a565b5f61391c6001600160a01b03841683613969565b905080515f1415801561394057508080602001905181019061393e91906140c7565b155b1561123157604051635274afe760e01b81526001600160a01b0384166004820152602401610c4b565b6060610bc683835f845f5f856001600160a01b0316848660405161398d91906140e6565b5f6040518083038185875af1925050503d805f81146139c7576040519150601f19603f3d011682016040523d82523d5f602084013e6139cc565b606091505b50915091506139dc8683836139e6565b9695505050505050565b6060826139fb576139f682613a42565b610bc6565b8151158015613a1257506001600160a01b0384163b155b15613a3b57604051639996b31560e01b81526001600160a01b0385166004820152602401610c4b565b5080610bc6565b805115613a525780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b828054828255905f5260205f20908101928215613abe579160200282015b82811115613abe57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613a89565b50611d2e9291505b80821115611d2e575f8155600101613ac6565b6001600160a01b0381168114612243575f5ffd5b80356114c181613ad9565b5f60208284031215613b08575f5ffd5b8135610bc681613ad9565b5f5b83811015613b2d578181015183820152602001613b15565b50505f910152565b602081525f8251806020840152613b53816040850160208701613b13565b601f01601f19169190910160400192915050565b5f60208284031215613b77575f5ffd5b5035919050565b5f5f60408385031215613b8f575f5ffd5b8235613b9a81613ad9565b946020939093013593505050565b5f5f5f5f60808587031215613bbb575f5ffd5b8435613bc681613ad9565b966020860135965060408601359560600135945092505050565b5f5f5f60608486031215613bf2575f5ffd5b8335613bfd81613ad9565b92506020840135613c0d81613ad9565b929592945050506040919091013590565b5f5f60408385031215613c2f575f5ffd5b8235613c3a81613ad9565b91506020830135613c4a81613ad9565b809150509250929050565b5f5f60408385031215613c66575f5ffd5b823591506020830135613c4a81613ad9565b5f5f5f60608486031215613c8a575f5ffd5b505081359360208301359350604090920135919050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215613cc5575f5ffd5b813567ffffffffffffffff811115613cdb575f5ffd5b8201601f81018413613ceb575f5ffd5b803567ffffffffffffffff811115613d0557613d05613ca1565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d3257613d32613ca1565b604052918252602081840181019290810187841115613d4f575f5ffd5b6020850194505b83851015613d7557613d6785613aed565b815260209485019401613d56565b509695505050505050565b5f5f5f60608486031215613d92575f5ffd5b833592506020840135613da481613ad9565b91506040840135613db481613ad9565b809150509250925092565b602080825282518282018190525f918401906040840190835b81811015613dff5783516001600160a01b0316835260209384019390920191600101613dd8565b509095945050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b5257610b52613e0a565b600181811c90821680613e4557607f821691505b602082108103610bcd57634e487b7160e01b5f52602260045260245ffd5b81810381811115610b5257610b52613e0a565b5f60208284031215613e86575f5ffd5b8151610bc681613ad9565b60ff8281168282160390811115610b5257610b52613e0a565b634e487b7160e01b5f52603260045260245ffd5b5f60ff821660ff8103613ed357613ed3613e0a565b60010192915050565b5f60ff821680613eee57613eee613e0a565b5f190192915050565b60ff8181168382160190811115610b5257610b52613e0a565b6001600160a01b039390931683526020830191909152604082015260600190565b5f60208284031215613f41575f5ffd5b5051919050565b6001815b6001841115613f8357808504811115613f6757613f67613e0a565b6001841615613f7557908102905b60019390931c928002613f4c565b935093915050565b5f82613f9957506001610b52565b81613fa557505f610b52565b8160018114613fbb5760028114613fc557613fe1565b6001915050610b52565b60ff841115613fd657613fd6613e0a565b50506001821b610b52565b5060208310610133831016604e8410600b8410161715614004575081810a610b52565b6140105f198484613f48565b805f190482111561402357614023613e0a565b029392505050565b5f610bc660ff841683613f8b565b634e487b7160e01b5f52601260045260245ffd5b5f5f6040838503121561405e575f5ffd5b505080516020909101519092909150565b634e487b7160e01b5f52602160045260245ffd5b5f60ff8316806140a157634e487b7160e01b5f52601260045260245ffd5b8060ff84160691505092915050565b8082028115828204841417610b5257610b52613e0a565b5f602082840312156140d7575f5ffd5b81518015158114610bc6575f5ffd5b5f82516140f7818460208701613b13565b919091019291505056fea26469706673582212204e695dd3a83dc12d398f9fcb85a9480fab4d5e83b9cbe84928b15bbb0fdc9c1664736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889400000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000135969656c642043686173696e672055534443650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077963555344436500000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _asset (address): 0x29219dd400f2Bf60E5a23d13Be72B486D4038894
Arg [1] : _manager (address): 0x75cb5d555933fe86E0ac8975A623aCb5CEC13E28
Arg [2] : _protocolFeeRecipient (address): 0xd132631A63af5C616e60606025C8e5871ADdF76f
Arg [3] : _name (string): Yield Chasing USDCe
Arg [4] : _symbol (string): ycUSDCe
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894
Arg [1] : 00000000000000000000000075cb5d555933fe86e0ac8975a623acb5cec13e28
Arg [2] : 000000000000000000000000d132631a63af5c616e60606025c8e5871addf76f
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [6] : 5969656c642043686173696e6720555344436500000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [8] : 7963555344436500000000000000000000000000000000000000000000000000
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.