Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61014060 | 20089846 | 154 days ago | IN | 0 ETH | 0.05883907 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
REthHoldingStrategy
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 99999 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/IERC20.sol"; import "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "../external/interfaces/strategies/rEth/IREthToken.sol"; import "../external/interfaces/strategies/rEth/IRocketSwapRouter.sol"; import "../libraries/PackedRange.sol"; import "./Strategy.sol"; import "./helpers/WethHelper.sol"; error REthHoldingBeforeDepositCheckFailed(); error REthHoldingBeforeRedeemalCheckFailed(); error REthHoldingDepositSlippagesFailed(); error REthHoldingRedeemSlippagesFailed(); // one asset // no rewards // slippages // - mode selection: slippages[0] // - DHW with deposit: slippages[0] == 0 // - beforeDepositCheck: slippages[1] // - beforeRedeemalCheck: slippages[2] // - _depositToProtocol: slippages[3..6] // - DHW with withdrawal: slippages[0] == 1 // - beforeDepositCheck: slippages[1] // - beforeRedeemalCheck: slippages[2] // - _redeemFromProtocol: slippages[3..6] // - reallocate: slippages[0] == 2 // - beforeDepositCheck: depositSlippages[1] // - _depositToProtocol: depositSlippages[2..5] // - beforeRedeemalCheck: withdrawalSlippages[1] // - _redeemFromProtocol: withdrawalSlippages[2..5] // - redeemFast or emergencyWithdraw: slippages[0] == 3 // - _redeemFromProtocol or _emergencyWithdrawImpl: slippages[1..4] // - deposit and withdrawals from protocol require four slippages: // - 1: portion to swap on uniswap // - 2: portion to swap on balancer // - 3: min out // - 4: ideal out // Description: // This is a liquid staking derivative strategy where eth is staked with Rocket // pool to be used for spinning up validators. Users staking share is // represented by rETH. The value of rETH compared to eth is growing with // validation rewards collected by validators spinned up using the staked eth. // The strategy uses the Rocket swap router to buy and sell rETH for eth. The // swap calls have four parameters that control how exactly the swap takes // place; portion to swap on Uniswap, portion to swap on Balancer, minimal out // and ideal out. If ideal out is less then or equal to the internal rETH/eth // price, the router will swap as much as possible internally. What cannot be // swapped internally, will be swapped on Uniswap and Balancer, based on // portions specified. Note that the portions are arbitrary numbers, what // matters is just their ratio. At the end, the swapper checks if final swapped // amount is at least min out, otherwise the transaction reverts. These // parameters are passed in as slippages (see above for details). contract REthHoldingStrategy is Strategy, WethHelper { using SafeERC20 for IERC20; IRocketSwapRouter public immutable rocketSwapRouter; IREthToken public immutable rEthToken; uint256 private _lastSharePrice; constructor( IAssetGroupRegistry assetGroupRegistry_, ISpoolAccessControl accessControl_, uint256 assetGroupId_, IRocketSwapRouter rocketSwapRouter_, address weth_ ) Strategy(assetGroupRegistry_, accessControl_, assetGroupId_) WethHelper(weth_) { if (address(rocketSwapRouter_) == address(0)) { revert ConfigurationAddressZero(); } rocketSwapRouter = rocketSwapRouter_; rEthToken = IREthToken(rocketSwapRouter_.rETH()); } function initialize(string memory strategyName_) external initializer { __Strategy_init(strategyName_, NULL_ASSET_GROUP_ID); address[] memory tokens = _assetGroupRegistry.listAssetGroup(assetGroupId()); if (tokens.length != 1 || tokens[0] != weth) { revert InvalidAssetGroup(assetGroupId()); } _lastSharePrice = _getSharePrice(); } function assetRatio() external pure override returns (uint256[] memory) { uint256[] memory _assetRatio = new uint256[](1); _assetRatio[0] = 1; return _assetRatio; } function getUnderlyingAssetAmounts() external view returns (uint256[] memory amounts) { amounts = new uint256[](1); amounts[0] = rEthToken.getEthValue(rEthToken.balanceOf(address(this))); } function beforeDepositCheck(uint256[] memory amounts, uint256[] calldata slippages) public override { if (_isViewExecution()) { uint256[] memory beforeDepositCheckSlippageAmounts = new uint256[](1); beforeDepositCheckSlippageAmounts[0] = amounts[0]; emit BeforeDepositCheckSlippages(beforeDepositCheckSlippageAmounts); return; } if (slippages[0] > 2) { revert REthHoldingBeforeDepositCheckFailed(); } if (!PackedRange.isWithinRange(slippages[1], amounts[0])) { revert REthHoldingBeforeDepositCheckFailed(); } } function beforeRedeemalCheck(uint256 ssts, uint256[] calldata slippages) public override { if (_isViewExecution()) { emit BeforeRedeemalCheckSlippages(ssts); return; } uint256 slippage; if (slippages[0] < 2) { slippage = slippages[2]; } else if (slippages[0] == 2) { slippage = slippages[1]; } else { revert REthHoldingBeforeRedeemalCheckFailed(); } if (!PackedRange.isWithinRange(slippage, ssts)) { revert REthHoldingBeforeRedeemalCheckFailed(); } } function _depositToProtocol(address[] calldata, uint256[] memory amounts, uint256[] calldata slippages) internal override { uint256 slippageOffset; if (slippages[0] == 0) { slippageOffset = 3; } else if (slippages[0] == 2) { slippageOffset = 2; } else { revert REthHoldingDepositSlippagesFailed(); } _depositInternal(amounts[0], slippages, slippageOffset); } function _redeemFromProtocol(address[] calldata, uint256 ssts, uint256[] calldata slippages) internal override { uint256 slippageOffset; if (slippages[0] == 1) { slippageOffset = 3; } else if (slippages[0] == 2) { slippageOffset = 2; } else if (slippages[0] == 3) { slippageOffset = 1; } else if (slippages[0] == 0 && _isViewExecution()) { slippageOffset = 3; } else { revert REthHoldingRedeemSlippagesFailed(); } uint256 sharesToRedeem = rEthToken.balanceOf(address(this)) * ssts / totalSupply(); _redeemInternal(sharesToRedeem, slippages, slippageOffset); } function _emergencyWithdrawImpl(uint256[] calldata slippages, address recipient) internal override { if (slippages[0] != 3) { revert REthHoldingRedeemSlippagesFailed(); } uint256 bought = _redeemInternal(rEthToken.balanceOf(address(this)), slippages, 1); IERC20(weth).safeTransfer(recipient, bought); } function _compound(address[] calldata, SwapInfo[] calldata, uint256[] calldata) internal pure override returns (int256) {} function _getYieldPercentage(int256) internal override returns (int256 baseYieldPercentage) { uint256 currentSharePrice = _getSharePrice(); baseYieldPercentage = _calculateYieldPercentage(_lastSharePrice, currentSharePrice); _lastSharePrice = currentSharePrice; } function _swapAssets(address[] memory, uint256[] memory, SwapInfo[] calldata) internal override {} function _getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager) internal view override returns (uint256) { uint256 assets = rEthToken.getEthValue(rEthToken.balanceOf(address(this))); return priceFeedManager.assetToUsdCustomPrice(weth, assets, exchangeRates[0]); } function _depositInternal(uint256 amount, uint256[] memory slippages, uint256 startingSlippage) private returns (uint256 bought) { if (_isViewExecution()) { // optimize first (uint256[2] memory portions, uint256 amountOut) = rocketSwapRouter.optimiseSwapTo(amount, 10); slippages[startingSlippage] = portions[0]; slippages[startingSlippage + 1] = portions[1]; slippages[startingSlippage + 2] = amountOut; slippages[startingSlippage + 3] = amountOut; uint256[] memory depositSlippages = new uint256[](4); depositSlippages[0] = portions[0]; depositSlippages[1] = portions[1]; depositSlippages[2] = amountOut; depositSlippages[3] = amountOut; emit Slippages(true, 0, abi.encode(depositSlippages)); } unwrapEth(amount); bought = rEthToken.balanceOf(address(this)); rocketSwapRouter.swapTo{value: amount}( slippages[startingSlippage], slippages[startingSlippage + 1], slippages[startingSlippage + 2], slippages[startingSlippage + 3] ); bought = rEthToken.balanceOf(address(this)) - bought; } function _redeemInternal(uint256 amount, uint256[] memory slippages, uint256 startingSlippage) private returns (uint256 bought) { if (_isViewExecution()) { // optimize first (uint256[2] memory portions, uint256 amountOut) = rocketSwapRouter.optimiseSwapFrom(amount, 10); slippages[startingSlippage] = portions[0]; slippages[startingSlippage + 1] = portions[1]; slippages[startingSlippage + 2] = amountOut; slippages[startingSlippage + 3] = amountOut; uint256[] memory withdrawalSlippages = new uint256[](4); withdrawalSlippages[0] = portions[0]; withdrawalSlippages[1] = portions[1]; withdrawalSlippages[2] = amountOut; withdrawalSlippages[3] = amountOut; emit Slippages(false, 0, abi.encode(withdrawalSlippages)); } _resetAndApprove(IERC20(address(rEthToken)), address(rocketSwapRouter), amount); rocketSwapRouter.swapFrom( slippages[startingSlippage], slippages[startingSlippage + 1], slippages[startingSlippage + 2], slippages[startingSlippage + 3], amount ); bought = address(this).balance; wrapEth(bought); } function _getSharePrice() private view returns (uint256) { return rEthToken.getEthValue(1 ether); } function _getProtocolRewardsInternal() internal virtual override returns (address[] memory tokens, uint256[] memory amounts) {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.17; interface IREthToken { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function getEthValue(uint256 _rethAmount) external view returns (uint256); function getRethValue(uint256 _ethAmount) external view returns (uint256); function getTotalCollateral() external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.17; interface IRocketSwapRouter { function rETH() external view returns (address); function swapTo(uint256 _uniswapPortion, uint256 _balancerPortion, uint256 _minTokensOut, uint256 _idealTokensOut) external payable; function swapFrom( uint256 _uniswapPortion, uint256 _balancerPortion, uint256 _minTokensOut, uint256 _idealTokensOut, uint256 _tokensIn ) external; function optimiseSwapTo(uint256 _amount, uint256 _steps) external returns (uint256[2] memory portions, uint256 amountOut); function optimiseSwapFrom(uint256 _amount, uint256 _steps) external returns (uint256[2] memory portions, uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; library PackedRange { uint256 constant BITS = 128; uint256 constant MAX = (1 << BITS) - 1; function isWithinRange(uint256 range, uint256 value) internal pure returns (bool) { return !((value < (range & MAX)) || (value > (range >> BITS))); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/utils/math/Math.sol"; import "@openzeppelin-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "../interfaces/Constants.sol"; import "../interfaces/IAssetGroupRegistry.sol"; import "../interfaces/IMasterWallet.sol"; import "../interfaces/IStrategy.sol"; import "../interfaces/IStrategyRegistry.sol"; import "../interfaces/CommonErrors.sol"; import "../interfaces/Constants.sol"; import "../access/SpoolAccessControllable.sol"; /** * @notice Used when initial locked strategy shares are already minted and strategy usd value is zero. */ error StrategyWorthIsZero(); abstract contract Strategy is ERC20Upgradeable, SpoolAccessControllable, IStrategy { using SafeERC20 for IERC20; /* ========== STATE VARIABLES ========== */ IAssetGroupRegistry internal immutable _assetGroupRegistry; /// @notice Name of the strategy string private _strategyName; /// @dev ID of the asset group used by the strategy. uint256 private immutable _assetGroupId; /// @dev ID of the asset group used by the strategy. uint256 private _assetGroupIdStorage; // Only one of the above can be set. Use the `assetGroupId` function to read // the correct one. constructor(IAssetGroupRegistry assetGroupRegistry_, ISpoolAccessControl accessControl_, uint256 assetGroupId_) SpoolAccessControllable(accessControl_) { if (address(assetGroupRegistry_) == address(0)) { revert ConfigurationAddressZero(); } _assetGroupRegistry = assetGroupRegistry_; _assetGroupId = assetGroupId_; } function __Strategy_init(string memory strategyName_, uint256 assetGroupId_) internal onlyInitializing { if (bytes(strategyName_).length == 0) revert InvalidConfiguration(); // asset group ID needs to be set exactly once, // either in constructor or initializer if (_assetGroupId == NULL_ASSET_GROUP_ID) { if (assetGroupId_ == NULL_ASSET_GROUP_ID) { revert InvalidAssetGroupIdInitialization(); } _assetGroupIdStorage = assetGroupId_; } else { if (assetGroupId_ != NULL_ASSET_GROUP_ID) { revert InvalidAssetGroupIdInitialization(); } } _assetGroupRegistry.validateAssetGroup(assetGroupId()); _strategyName = strategyName_; __ERC20_init("Strategy Share Token", "SST"); } /* ========== EXTERNAL VIEW FUNCTIONS ========== */ function assetGroupId() public view returns (uint256) { return _assetGroupId > 0 ? _assetGroupId : _assetGroupIdStorage; } function assets() public view returns (address[] memory) { return _assetGroupRegistry.listAssetGroup(assetGroupId()); } function assetRatio() external view virtual returns (uint256[] memory); function strategyName() external view returns (string memory) { return _strategyName; } function beforeDepositCheck(uint256[] memory amounts, uint256[] calldata slippages) public virtual; function beforeRedeemalCheck(uint256 ssts, uint256[] calldata slippages) public virtual; /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ function doHardWork(StrategyDhwParameterBag calldata dhwParams) external returns (DhwInfo memory dhwInfo) { _checkRole(ROLE_STRATEGY_REGISTRY, msg.sender); bool depositNeeded; uint256[] memory assetsToDeposit = new uint256[](dhwParams.assetGroup.length); unchecked { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { assetsToDeposit[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this)); if (assetsToDeposit[i] > 0) { depositNeeded = true; } } } beforeDepositCheck(assetsToDeposit, dhwParams.slippages); beforeRedeemalCheck(dhwParams.withdrawnShares, dhwParams.slippages); // usdWorth[0]: usd worth before deposit / withdrawal // usdWorth[1]: usd worth after deposit / withdrawal uint256[2] memory usdWorth; // Compound and get USD value. { dhwInfo.yieldPercentage = _getYieldPercentage(dhwParams.baseYield); int256 compoundYield = _compound(dhwParams.assetGroup, dhwParams.compoundSwapInfo, dhwParams.slippages); dhwInfo.yieldPercentage += compoundYield + compoundYield * dhwInfo.yieldPercentage / YIELD_FULL_PERCENT_INT; } // collect fees, mint SVTs relative to the yield generated _collectPlatformFees(dhwInfo.yieldPercentage, dhwParams.platformFees); usdWorth[0] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager); uint256 matchedShares; uint256 depositShareEquivalent; uint256 mintedShares; uint256 withdrawnShares = dhwParams.withdrawnShares; // Calculate deposit share equivalent. if (depositNeeded) { uint256 valueToDeposit = dhwParams.priceFeedManager.assetToUsdCustomPriceBulk( dhwParams.assetGroup, assetsToDeposit, dhwParams.exchangeRates ); if (totalSupply() < INITIAL_LOCKED_SHARES) { depositShareEquivalent = INITIAL_SHARE_MULTIPLIER * valueToDeposit; } else if (usdWorth[0] > 0) { depositShareEquivalent = totalSupply() * valueToDeposit / usdWorth[0]; } else { revert StrategyWorthIsZero(); } // Match withdrawals and deposits by taking smaller value as matched shares. if (depositShareEquivalent < withdrawnShares) { matchedShares = depositShareEquivalent; } else { matchedShares = withdrawnShares; } } uint256[] memory withdrawnAssets = new uint256[](dhwParams.assetGroup.length); bool withdrawn; if (depositShareEquivalent > withdrawnShares) { // Deposit is needed. // - match if needed if (matchedShares > 0) { unchecked { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { withdrawnAssets[i] = assetsToDeposit[i] * matchedShares / depositShareEquivalent; assetsToDeposit[i] -= withdrawnAssets[i]; } } withdrawn = true; } // - swap assets uint256[] memory assetsIn = new uint256[](assetsToDeposit.length); if (dhwParams.swapInfo.length > 0) { _swapAssets(dhwParams.assetGroup, assetsToDeposit, dhwParams.swapInfo); for (uint256 i; i < dhwParams.assetGroup.length; ++i) { assetsIn[i] = assetsToDeposit[i]; assetsToDeposit[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this)) - withdrawnAssets[i]; } } else { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { assetsIn[i] = assetsToDeposit[i]; } } // - deposit assets into the protocol _depositToProtocol(dhwParams.assetGroup, assetsToDeposit, dhwParams.slippages); usdWorth[1] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager); // - mint SSTs mintedShares = _mintStrategyShares(usdWorth[0], usdWorth[1]); emit Deposited(mintedShares, usdWorth[1] - usdWorth[0], assetsIn, assetsToDeposit); mintedShares += matchedShares; } else if (withdrawnShares > depositShareEquivalent) { // Withdrawal is needed. // - match if needed if (matchedShares > 0) { unchecked { withdrawnShares -= matchedShares; mintedShares = matchedShares; } } // - redeem shares from protocol _redeemFromProtocol(dhwParams.assetGroup, withdrawnShares, dhwParams.slippages); _burn(address(this), withdrawnShares); withdrawn = true; // - figure out how much was withdrawn usdWorth[1] = _getUsdWorth(dhwParams.exchangeRates, dhwParams.priceFeedManager); unchecked { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { withdrawnAssets[i] = IERC20(dhwParams.assetGroup[i]).balanceOf(address(this)); } } emit Withdrawn(withdrawnShares, usdWorth[1], withdrawnAssets); } else { // Neither withdrawal nor deposit is needed. // - match if needed if (matchedShares > 0) { mintedShares = withdrawnShares; unchecked { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { withdrawnAssets[i] = assetsToDeposit[i]; } } withdrawn = true; } usdWorth[1] = usdWorth[0]; } // Transfer withdrawn assets to master wallet if needed. if (withdrawn) { unchecked { for (uint256 i; i < dhwParams.assetGroup.length; ++i) { IERC20(dhwParams.assetGroup[i]).safeTransfer(dhwParams.masterWallet, withdrawnAssets[i]); } } } dhwInfo.sharesMinted = mintedShares; dhwInfo.assetsWithdrawn = withdrawnAssets; dhwInfo.valueAtDhw = usdWorth[1]; dhwInfo.totalSstsAtDhw = totalSupply(); } function redeemFast( uint256 shares, address masterWallet, address[] calldata assetGroup, uint256[] calldata slippages ) external returns (uint256[] memory) { if ( !_accessControl.hasRole(ROLE_SMART_VAULT_MANAGER, msg.sender) && !_accessControl.hasRole(ROLE_STRATEGY_REGISTRY, msg.sender) ) { revert NotFastRedeemer(msg.sender); } return _redeemShares(shares, address(this), masterWallet, assetGroup, slippages); } function redeemShares(uint256 shares, address redeemer, address[] calldata assetGroup, uint256[] calldata slippages) external returns (uint256[] memory) { _checkRole(ROLE_STRATEGY_REGISTRY, msg.sender); return _redeemShares(shares, redeemer, redeemer, assetGroup, slippages); } /// @dev is only called when reallocating function depositFast( address[] calldata assetGroup, uint256[] calldata exchangeRates, IUsdPriceFeedManager priceFeedManager, uint256[] calldata slippages, SwapInfo[] calldata swapInfo ) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) returns (uint256) { // get amount of assets available to deposit uint256[] memory assetsToDeposit = new uint256[](assetGroup.length); for (uint256 i; i < assetGroup.length; ++i) { assetsToDeposit[i] = IERC20(assetGroup[i]).balanceOf(address(this)); } // swap assets _swapAssets(assetGroup, assetsToDeposit, swapInfo); uint256[] memory assetsDeposited = new uint256[](assetGroup.length); for (uint256 i; i < assetGroup.length; ++i) { assetsDeposited[i] = IERC20(assetGroup[i]).balanceOf(address(this)); } // deposit assets uint256 usdWorth0 = _getUsdWorth(exchangeRates, priceFeedManager); _depositToProtocol(assetGroup, assetsDeposited, slippages); uint256 usdWorth1 = _getUsdWorth(exchangeRates, priceFeedManager); // mint SSTs uint256 sstsToMint = _mintStrategyShares(usdWorth0, usdWorth1); emit Deposited(sstsToMint, usdWorth1 - usdWorth0, assetsToDeposit, assetsDeposited); return sstsToMint; } function claimShares(address smartVault, uint256 amount) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) { _transfer(address(this), smartVault, amount); } function releaseShares(address smartVault, uint256 amount) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) { _transfer(smartVault, address(this), amount); } function emergencyWithdraw(uint256[] calldata slippages, address recipient) external onlyRole(ROLE_STRATEGY_REGISTRY, msg.sender) { _emergencyWithdrawImpl(slippages, recipient); } function getProtocolRewards() external onlyViewExecution returns (address[] memory, uint256[] memory) { return _getProtocolRewardsInternal(); } function getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager) external onlyRole(ROLE_SMART_VAULT_MANAGER, msg.sender) returns (uint256) { return _getUsdWorth(exchangeRates, priceFeedManager); } /* ========== PRIVATE/INTERNAL FUNCTIONS ========== */ function _mintStrategyShares(uint256 usdWorthBefore, uint256 usdWorthAfter) private returns (uint256 mintedShares) { uint256 totalSupply_ = totalSupply(); if (totalSupply_ < INITIAL_LOCKED_SHARES) { // multiply with usd worth after deposit as there are no other owned shares mintedShares = usdWorthAfter * INITIAL_SHARE_MULTIPLIER; unchecked { uint256 lockedSharesLeftToMint = INITIAL_LOCKED_SHARES - totalSupply_; if (mintedShares < lockedSharesLeftToMint) { lockedSharesLeftToMint = mintedShares; } mintedShares -= lockedSharesLeftToMint; _mint(INITIAL_LOCKED_SHARES_ADDRESS, lockedSharesLeftToMint); } } else if (usdWorthBefore > 0) { mintedShares = (usdWorthAfter - usdWorthBefore) * totalSupply_ / usdWorthBefore; } else { revert StrategyWorthIsZero(); } _mint(address(this), mintedShares); } function _redeemShares( uint256 shares, address shareOwner, address recipient, address[] calldata assetGroup, uint256[] calldata slippages ) internal virtual returns (uint256[] memory) { // redeem shares from protocol uint256[] memory assetsWithdrawn = _redeemFromProtocolAndReturnAssets(assetGroup, shares, slippages); _burn(shareOwner, shares); // transfer assets to recipient (master wallet in case of redeemFast) unchecked { for (uint256 i; i < assetGroup.length; ++i) { IERC20(assetGroup[i]).safeTransfer(recipient, assetsWithdrawn[i]); } } return assetsWithdrawn; } /** * @notice Calculate and mint platform performance fees based on the yield generated. * @param yieldPct Yield generated since previous DHW. Full percent is `YIELD_FULL_PERCENT`. * @param platformFees Platform fees info, containing information of the sice and recipient of the fees (SSTs). * @return sharesMinted Returns newly minted shares representing the platform performance fees. */ function _collectPlatformFees(int256 yieldPct, PlatformFees calldata platformFees) internal virtual returns (uint256 sharesMinted) { if (yieldPct > 0) { uint256 uint256YieldPct = uint256(yieldPct); uint256 yieldPctUsersPlusOne = uint256YieldPct * (FULL_PERCENT - platformFees.ecosystemFeePct - platformFees.treasuryFeePct) + FULL_PERCENT * YIELD_FULL_PERCENT; uint256 totalSupplyTimesYieldPct = totalSupply() * uint256YieldPct; // mint new ecosystem fee SSTs uint256 newEcosystemFeeSsts = totalSupplyTimesYieldPct * platformFees.ecosystemFeePct / yieldPctUsersPlusOne; _mint(platformFees.ecosystemFeeReceiver, newEcosystemFeeSsts); // mint new treasury fee SSTs uint256 newTreasuryFeeSsts = totalSupplyTimesYieldPct * platformFees.treasuryFeePct / yieldPctUsersPlusOne; _mint(platformFees.treasuryFeeReceiver, newTreasuryFeeSsts); unchecked { sharesMinted = newEcosystemFeeSsts + newTreasuryFeeSsts; } emit PlatformFeesCollected(address(this), sharesMinted); } } function _redeemFromProtocolAndReturnAssets(address[] calldata tokens, uint256 ssts, uint256[] calldata slippages) internal virtual returns (uint256[] memory withdrawnAssets) { withdrawnAssets = new uint256[](tokens.length); for (uint256 i; i < tokens.length; ++i) { withdrawnAssets[i] = IERC20(tokens[i]).balanceOf(address(this)); } _redeemFromProtocol(tokens, ssts, slippages); for (uint256 i; i < tokens.length; ++i) { withdrawnAssets[i] = IERC20(tokens[i]).balanceOf(address(this)) - withdrawnAssets[i]; } } function _calculateYieldPercentage(uint256 previousValue, uint256 currentValue) internal pure returns (int256 yieldPercentage) { if (currentValue > previousValue) { yieldPercentage = int256((currentValue - previousValue) * YIELD_FULL_PERCENT / previousValue); } else if (previousValue > currentValue) { yieldPercentage = -int256((previousValue - currentValue) * YIELD_FULL_PERCENT / previousValue); } } function _resetAndApprove(IERC20 token, address spender, uint256 amount) internal { _resetAllowance(token, spender); token.safeApprove(spender, amount); } function _resetAllowance(IERC20 token, address spender) internal { if (token.allowance(address(this), spender) > 0) { token.safeApprove(spender, 0); } } function _isViewExecution() internal view returns (bool) { return tx.origin == address(0); } function _compound(address[] calldata tokens, SwapInfo[] calldata compoundSwapInfo, uint256[] calldata slippages) internal virtual returns (int256 compoundYield); function _getYieldPercentage(int256 manualYield) internal virtual returns (int256); /** * @dev Swaps assets. * @param tokens Addresses of tokens to swap. * @param toSwap Available amounts to swap. * @param swapInfo Information on how to swap. */ function _swapAssets(address[] memory tokens, uint256[] memory toSwap, SwapInfo[] calldata swapInfo) internal virtual; /** * @dev Deposits assets into the underlying protocol. * @param tokens Addresses of asset tokens. * @param amounts Amounts to deposit. * @param slippages Slippages to guard depositing. */ function _depositToProtocol(address[] calldata tokens, uint256[] memory amounts, uint256[] calldata slippages) internal virtual; /** * @dev Redeems shares from the undelying protocol. * @param tokens Addresses of asset tokens. * @param ssts Amount of strategy tokens to redeem. * @param slippages Slippages to guard redeemal. */ function _redeemFromProtocol(address[] calldata tokens, uint256 ssts, uint256[] calldata slippages) internal virtual; function _emergencyWithdrawImpl(uint256[] calldata slippages, address recipient) internal virtual; function _getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager) internal virtual returns (uint256); /** * @dev Gets protocol rewards. * @return tokens Addresses of reward tokens. * @return amounts Amount of each reward token. */ function _getProtocolRewardsInternal() internal virtual returns (address[] memory tokens, uint256[] memory amounts); /* ========== MODIFIERS ========== */ modifier onlyViewExecution() { require(_isViewExecution()); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/IERC20.sol"; import "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "../../external/interfaces/weth/IWETH9.sol"; import "../../interfaces/CommonErrors.sol"; abstract contract WethHelper { using SafeERC20 for IERC20; /** * @notice Address of wrapped eth contract. */ address public immutable weth; /** * @param weth_ Address of wrapped eth contract. */ constructor(address weth_) { if (address(weth_) == address(0)) { revert ConfigurationAddressZero(); } weth = weth_; } /** * @notice Wraps eth. * @param amount Amount of eth to wrap. */ function wrapEth(uint256 amount) internal { IWETH9(weth).deposit{value: amount}(); } /** * @notice Unwraps eth. * @param amount Amount of eth to unwrap. */ function unwrapEth(uint256 amount) internal { IWETH9(weth).withdraw(amount); } receive() external payable {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20Upgradeable.sol"; import "./extensions/IERC20MetadataUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[45] private __gap; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /// @dev Number of seconds in an average year. uint256 constant SECONDS_IN_YEAR = 31_556_926; /// @dev Number of seconds in an average year. int256 constant SECONDS_IN_YEAR_INT = 31_556_926; /// @dev Represents 100%. uint256 constant FULL_PERCENT = 100_00; /// @dev Represents 100%. int256 constant FULL_PERCENT_INT = 100_00; /// @dev Represents 100% for yield. int256 constant YIELD_FULL_PERCENT_INT = 10 ** 12; /// @dev Represents 100% for yield. uint256 constant YIELD_FULL_PERCENT = uint256(YIELD_FULL_PERCENT_INT); /// @dev Maximal management fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant MANAGEMENT_FEE_MAX = 5_00; /// @dev Maximal deposit fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant DEPOSIT_FEE_MAX = 5_00; /// @dev Maximal smart vault performance fee that can be set on a smart vault. Expressed in terms of FULL_PERCENT. uint256 constant SV_PERFORMANCE_FEE_MAX = 20_00; /// @dev Maximal ecosystem fee that can be set on the system. Expressed in terms of FULL_PERCENT. uint256 constant ECOSYSTEM_FEE_MAX = 20_00; /// @dev Maximal treasury fee that can be set on the system. Expressed in terms of FULL_PERCENT. uint256 constant TREASURY_FEE_MAX = 10_00; /// @dev Maximal risk score a strategy can be assigned. uint8 constant MAX_RISK_SCORE = 10_0; /// @dev Minimal risk score a strategy can be assigned. uint8 constant MIN_RISK_SCORE = 1; /// @dev Maximal value for risk tolerance a smart vautl can have. int8 constant MAX_RISK_TOLERANCE = 10; /// @dev Minimal value for risk tolerance a smart vault can have. int8 constant MIN_RISK_TOLERANCE = -10; /// @dev If set as risk provider, system will return fixed risk score values address constant STATIC_RISK_PROVIDER = address(0xaaa); /// @dev Fixed values to use if risk provider is set to STATIC_RISK_PROVIDER uint8 constant STATIC_RISK_SCORE = 1; /// @dev Maximal value of deposit NFT ID. uint256 constant MAXIMAL_DEPOSIT_ID = 2 ** 255; /// @dev Maximal value of withdrawal NFT ID. uint256 constant MAXIMAL_WITHDRAWAL_ID = 2 ** 256 - 1; /// @dev How many shares will be minted with a NFT uint256 constant NFT_MINTED_SHARES = 10 ** 6; /// @dev Each smart vault can have up to STRATEGY_COUNT_CAP strategies. uint256 constant STRATEGY_COUNT_CAP = 16; /// @dev Maximal DHW base yield. Expressed in terms of FULL_PERCENT. uint256 constant MAX_DHW_BASE_YIELD_LIMIT = 10_00; /// @dev Smart vault and strategy share multiplier at first deposit. uint256 constant INITIAL_SHARE_MULTIPLIER = 1000; /// @dev Strategy initial locked shares. These shares will never be unlocked. uint256 constant INITIAL_LOCKED_SHARES = 10 ** 12; /// @dev Strategy initial locked shares address. address constant INITIAL_LOCKED_SHARES_ADDRESS = address(0xdead); /// @dev Maximum number of guards a smart vault can be configured with uint256 constant MAX_GUARD_COUNT = 10; /// @dev Maximum number of actions a smart vault can be configured with uint256 constant MAX_ACTION_COUNT = 10; /// @dev ID of null asset group. Should not be used by any strategy or smart vault. uint256 constant NULL_ASSET_GROUP_ID = 0;
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/IERC20.sol"; /* ========== ERRORS ========== */ /** * @notice Used when invalid ID for asset group is provided. * @param assetGroupId Invalid ID for asset group. */ error InvalidAssetGroup(uint256 assetGroupId); /** * @notice Used when no assets are provided for an asset group. */ error NoAssetsProvided(); /** * @notice Used when token is not allowed to be used as an asset. * @param token Address of the token that is not allowed. */ error TokenNotAllowed(address token); /** * @notice Used when asset group already exists. * @param assetGroupId ID of the already existing asset group. */ error AssetGroupAlreadyExists(uint256 assetGroupId); /** * @notice Used when given array is unsorted. */ error UnsortedArray(); /* ========== INTERFACES ========== */ interface IAssetGroupRegistry { /* ========== EVENTS ========== */ /** * @notice Emitted when token is allowed to be used as an asset. * @param token Address of newly allowed token. */ event TokenAllowed(address indexed token); /** * @notice Emitted when asset group is registered. * @param assetGroupId ID of the newly registered asset group. */ event AssetGroupRegistered(uint256 indexed assetGroupId); /* ========== VIEW FUNCTIONS ========== */ /** * @notice Checks if token is allowed to be used as an asset. * @param token Address of token to check. * @return isAllowed True if token is allowed, false otherwise. */ function isTokenAllowed(address token) external view returns (bool isAllowed); /** * @notice Gets number of registered asset groups. * @return count Number of registered asset groups. */ function numberOfAssetGroups() external view returns (uint256 count); /** * @notice Gets asset group by its ID. * @dev Requirements: * - must provide a valid ID for the asset group * @return assets Array of assets in the asset group. */ function listAssetGroup(uint256 assetGroupId) external view returns (address[] memory assets); /** * @notice Gets asset group length. * @dev Requirements: * - must provide a valid ID for the asset group * @return length */ function assetGroupLength(uint256 assetGroupId) external view returns (uint256 length); /** * @notice Validates that provided ID represents an asset group. * @dev Function reverts when ID does not represent an asset group. * @param assetGroupId ID to validate. */ function validateAssetGroup(uint256 assetGroupId) external view; /** * @notice Checks if asset group composed of assets already exists. * Will revert if provided assets cannot form an asset group. * @param assets Assets composing the asset group. * @return Asset group ID if such asset group exists, 0 otherwise. */ function checkAssetGroupExists(address[] calldata assets) external view returns (uint256); /* ========== MUTATIVE FUNCTIONS ========== */ /** * @notice Allows a token to be used as an asset. * @dev Requirements: * - can only be called by the ROLE_SPOOL_ADMIN * @param token Address of token to be allowed. */ function allowToken(address token) external; /** * @notice Allows tokens to be used as assets. * @dev Requirements: * - can only be called by the ROLE_SPOOL_ADMIN * @param tokens Addresses of tokens to be allowed. */ function allowTokenBatch(address[] calldata tokens) external; /** * @notice Registers a new asset group. * @dev Requirements: * - must provide at least one asset * - all assets must be allowed * - assets must be sorted * - such asset group should not exist yet * - can only be called by the ROLE_SPOOL_ADMIN * @param assets Array of assets in the asset group. * @return id Sequential ID assigned to the asset group. */ function registerAssetGroup(address[] calldata assets) external returns (uint256 id); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/IERC20.sol"; interface IMasterWallet { /** * @notice Transfers amount of token to the recipient. * @dev Requirements: * - caller must have role ROLE_MASTER_WALLET_MANAGER * @param token Token to transfer. * @param recipient Target of the transfer. * @param amount Amount to transfer. */ function transfer(IERC20 token, address recipient, uint256 amount) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/IERC20.sol"; import "@openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {PlatformFees} from "./IStrategyRegistry.sol"; import "./ISwapper.sol"; import "./IUsdPriceFeedManager.sol"; /** * @notice Struct holding information how to swap the assets. * @custom:member slippage minumum output amount * @custom:member path swap path, first byte represents an action (e.g. Uniswap V2 custom swap), rest is swap specific path */ struct SwapData { uint256 slippage; // min amount out bytes path; // 1st byte is action, then path } /** * @notice Parameters for calling do hard work on strategy. * @custom:member swapInfo Information for swapping assets before depositing into the protocol. * @custom:member swapInfo Information for swapping rewards before depositing them back into the protocol. * @custom:member slippages Slippages used to constrain depositing and withdrawing from the protocol. * @custom:member assetGroup Asset group of the strategy. * @custom:member exchangeRates Exchange rates for assets. * @custom:member withdrawnShares Strategy shares withdrawn by smart vault. * @custom:member masterWallet Master wallet. * @custom:member priceFeedManager Price feed manager. * @custom:member baseYield Base yield value, manual input for specific strategies. * @custom:member platformFees Platform fees info. */ struct StrategyDhwParameterBag { SwapInfo[] swapInfo; SwapInfo[] compoundSwapInfo; uint256[] slippages; address[] assetGroup; uint256[] exchangeRates; uint256 withdrawnShares; address masterWallet; IUsdPriceFeedManager priceFeedManager; int256 baseYield; PlatformFees platformFees; } /** * @notice Information about results of the do hard work. * @custom:member sharesMinted Amount of strategy shares minted. * @custom:member assetsWithdrawn Amount of assets withdrawn. * @custom:member yieldPercentage Yield percentage from the previous DHW. * @custom:member valueAtDhw Value of the strategy at the end of DHW. * @custom:member totalSstsAtDhw Total SSTs at the end of DHW. */ struct DhwInfo { uint256 sharesMinted; uint256[] assetsWithdrawn; int256 yieldPercentage; uint256 valueAtDhw; uint256 totalSstsAtDhw; } /** * @notice Used when ghost strategy is called. */ error IsGhostStrategy(); /** * @notice Used when user is not allowed to redeem fast. * @param user User that tried to redeem fast. */ error NotFastRedeemer(address user); /** * @notice Used when asset group ID is not correctly initialized. */ error InvalidAssetGroupIdInitialization(); interface IStrategy is IERC20Upgradeable { /* ========== EVENTS ========== */ event Deposited( uint256 mintedShares, uint256 usdWorthDeposited, uint256[] assetsBeforeSwap, uint256[] assetsDeposited ); event Withdrawn(uint256 withdrawnShares, uint256 usdWorthWithdrawn, uint256[] withdrawnAssets); event PlatformFeesCollected(address indexed strategy, uint256 sharesMinted); event Slippages(bool isDeposit, uint256 slippage, bytes data); event BeforeDepositCheckSlippages(uint256[] amounts); event BeforeRedeemalCheckSlippages(uint256 ssts); /* ========== VIEW FUNCTIONS ========== */ /** * @notice Gets strategy name. * @return name Name of the strategy. */ function strategyName() external view returns (string memory name); /** * @notice Gets required ratio between underlying assets. * @return ratio Required asset ratio for the strategy. */ function assetRatio() external view returns (uint256[] memory ratio); /** * @notice Gets asset group used by the strategy. * @return id ID of the asset group. */ function assetGroupId() external view returns (uint256 id); /** * @notice Gets underlying assets for the strategy. * @return assets Addresses of the underlying assets. */ function assets() external view returns (address[] memory assets); /** * @notice Gets underlying asset amounts for the strategy. * @return amounts Amounts of the underlying assets. */ function getUnderlyingAssetAmounts() external view returns (uint256[] memory amounts); /* ========== MUTATIVE FUNCTIONS ========== */ /** * @dev Performs slippages check before depositing. * @param amounts Amounts to be deposited. * @param slippages Slippages to check against. */ function beforeDepositCheck(uint256[] memory amounts, uint256[] calldata slippages) external; /** * @dev Performs slippages check before redeemal. * @param ssts Amount of strategy tokens to be redeemed. * @param slippages Slippages to check against. */ function beforeRedeemalCheck(uint256 ssts, uint256[] calldata slippages) external; /** * @notice Does hard work: * - compounds rewards * - deposits into the protocol * - withdraws from the protocol * @dev Requirements: * - caller must have role ROLE_STRATEGY_REGISTRY * @param dhwParams Parameters for the do hard work. * @return info Information about do the performed hard work. */ function doHardWork(StrategyDhwParameterBag calldata dhwParams) external returns (DhwInfo memory info); /** * @notice Claims strategy shares after do-hard-work. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart vault claiming shares. * @param amount Amount of strategy shares to claim. */ function claimShares(address smartVault, uint256 amount) external; /** * @notice Releases shares back to strategy. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart vault releasing shares. * @param amount Amount of strategy shares to release. */ function releaseShares(address smartVault, uint256 amount) external; /** * @notice Instantly redeems strategy shares for assets. * @dev Requirements: * - caller must have either role ROLE_SMART_VAULT_MANAGER or role ROLE_STRATEGY_REGISTRY * @param shares Amount of shares to redeem. * @param masterWallet Address of the master wallet. * @param assetGroup Asset group of the strategy. * @param slippages Slippages to guard redeeming. * @return assetsWithdrawn Amount of assets withdrawn. */ function redeemFast( uint256 shares, address masterWallet, address[] calldata assetGroup, uint256[] calldata slippages ) external returns (uint256[] memory assetsWithdrawn); /** * @notice Instantly redeems strategy shares for assets. * @param shares Amount of shares to redeem. * @param redeemer Address of he redeemer, owner of SSTs. * @param assetGroup Asset group of the strategy. * @param slippages Slippages to guard redeeming. * @return assetsWithdrawn Amount of assets withdrawn. */ function redeemShares(uint256 shares, address redeemer, address[] calldata assetGroup, uint256[] calldata slippages) external returns (uint256[] memory assetsWithdrawn); /** * @notice Instantly deposits into the protocol. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param assetGroup Asset group of the strategy. * @param exchangeRates Asset to USD exchange rates. * @param priceFeedManager Price feed manager contract. * @param slippages Slippages to guard depositing. * @param swapInfo Information for swapping assets before depositing into the protocol. * @return sstsMinted Amount of SSTs minted. */ function depositFast( address[] calldata assetGroup, uint256[] calldata exchangeRates, IUsdPriceFeedManager priceFeedManager, uint256[] calldata slippages, SwapInfo[] calldata swapInfo ) external returns (uint256 sstsMinted); /** * @notice Instantly withdraws assets, bypassing shares mechanism. * Transfers withdrawn assets to the emergency withdrawal wallet. * @dev Requirements: * - caller must have role ROLE_STRATEGY_REGISTRY * @param slippages Slippages to guard redeeming. * @param recipient Recipient address */ function emergencyWithdraw(uint256[] calldata slippages, address recipient) external; /** * @notice Gets USD worth of the strategy. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param exchangeRates Asset to USD exchange rates. * @param priceFeedManager Price feed manager contract. */ function getUsdWorth(uint256[] memory exchangeRates, IUsdPriceFeedManager priceFeedManager) external returns (uint256 usdWorth); /** * @notice Gets protocol rewards. * @dev Requirements: * - can only be called in view-execution mode. * @return tokens Addresses of reward tokens. * @return amounts Amount of reward tokens available. */ function getProtocolRewards() external returns (address[] memory tokens, uint256[] memory amounts); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "./ISwapper.sol"; import {DhwInfo} from "./IStrategy.sol"; import "../libraries/uint16a16Lib.sol"; /* ========== ERRORS ========== */ /** * @notice Used when trying to register an already registered strategy. * @param address_ Address of already registered strategy. */ error StrategyAlreadyRegistered(address address_); /** * @notice Used when DHW was not run yet for a strategy index. * @param strategy Address of the strategy. * @param strategyIndex Index of the strategy. */ error DhwNotRunYetForIndex(address strategy, uint256 strategyIndex); /** * @notice Used when provided token list is invalid. */ error InvalidTokenList(); /** * @notice Used when ghost strategy is used. */ error GhostStrategyUsed(); /** * @notice Used when syncing vault that is already fully synced. */ error NothingToSync(); /** * @notice Used when system tries to configure a too large ecosystem fee. * @param ecosystemFeePct Requested ecosystem fee. */ error EcosystemFeeTooLarge(uint256 ecosystemFeePct); /** * @notice Used when system tries to configure a too large treasury fee. * @param treasuryFeePct Requested treasury fee. */ error TreasuryFeeTooLarge(uint256 treasuryFeePct); /** * @notice Used when user tries to re-add a strategy that was previously removed from the system. * @param strategy Strategy address */ error StrategyPreviouslyRemoved(address strategy); /** * @notice Represents change of state for a strategy during a DHW. * @custom:member exchangeRates Exchange rates between assets and USD. * @custom:member assetsDeposited Amount of assets deposited into the strategy. * @custom:member sharesMinted Amount of strategy shares minted. * @custom:member totalSSTs Amount of strategy shares at the end of the DHW. * @custom:member totalStrategyValue Total strategy value at the end of the DHW. * @custom:member dhwYields DHW yield percentage from the previous DHW. */ struct StrategyAtIndex { uint256[] exchangeRates; uint256[] assetsDeposited; uint256 sharesMinted; uint256 totalSSTs; uint256 totalStrategyValue; int256 dhwYields; } /** * @notice Parameters for calling do hard work. * @custom:member strategies Strategies to do-hard-worked upon, grouped by their asset group. * @custom:member swapInfo Information for swapping assets before depositing into protocol. SwapInfo[] per each strategy. * @custom:member compoundSwapInfo Information for swapping rewards before depositing them back into the protocol. SwapInfo[] per each strategy. * @custom:member strategySlippages Slippages used to constrain depositing into and withdrawing from the protocol. uint256[] per strategy. * @custom:member baseYields Base yield percentage the strategy created in the DHW period (applicable only for some strategies). * @custom:member tokens List of all asset tokens involved in the do hard work. * @custom:member exchangeRateSlippages Slippages used to constrain exchange rates for asset tokens. uint256[2] for each token. * @custom:member validUntil Sets the maximum timestamp the user is willing to wait to start executing 'do hard work'. */ struct DoHardWorkParameterBag { address[][] strategies; SwapInfo[][][] swapInfo; SwapInfo[][][] compoundSwapInfo; uint256[][][] strategySlippages; int256[][] baseYields; address[] tokens; uint256[2][] exchangeRateSlippages; uint256 validUntil; } /** * @notice Parameters for calling redeem fast. * @custom:member strategies Addresses of strategies. * @custom:member strategyShares Amount of shares to redeem. * @custom:member assetGroup Asset group of the smart vault. * @custom:member slippages Slippages to guard withdrawal. */ struct RedeemFastParameterBag { address[] strategies; uint256[] strategyShares; address[] assetGroup; uint256[][] withdrawalSlippages; } /** * @notice Group of platform fees. * @custom:member ecosystemFeeReciever Receiver of the ecosystem fees. * @custom:member ecosystemFeePct Ecosystem fees. Expressed in FULL_PERCENT. * @custom:member treasuryFeeReciever Receiver of the treasury fees. * @custom:member treasuryFeePct Treasury fees. Expressed in FULL_PERCENT. */ struct PlatformFees { address ecosystemFeeReceiver; uint96 ecosystemFeePct; address treasuryFeeReceiver; uint96 treasuryFeePct; } /* ========== INTERFACES ========== */ interface IStrategyRegistry { /* ========== EXTERNAL VIEW FUNCTIONS ========== */ /** * @notice Returns address of emergency withdrawal wallet. * @return emergencyWithdrawalWallet Address of the emergency withdrawal wallet. */ function emergencyWithdrawalWallet() external view returns (address emergencyWithdrawalWallet); /** * @notice Returns current do-hard-work indexes for strategies. * @param strategies Strategies. * @return dhwIndexes Current do-hard-work indexes for strategies. */ function currentIndex(address[] calldata strategies) external view returns (uint256[] memory dhwIndexes); /** * @notice Returns current strategy APYs. * @param strategies Strategies. */ function strategyAPYs(address[] calldata strategies) external view returns (int256[] memory apys); /** * @notice Returns assets deposited into a do-hard-work index for a strategy. * @param strategy Strategy. * @param dhwIndex Do-hard-work index. * @return assets Assets deposited into the do-hard-work index for the strategy. */ function depositedAssets(address strategy, uint256 dhwIndex) external view returns (uint256[] memory assets); /** * @notice Returns shares redeemed in a do-hard-work index for a strategy. * @param strategy Strategy. * @param dhwIndex Do-hard-work index. * @return shares Shares redeemed in a do-hard-work index for the strategy. */ function sharesRedeemed(address strategy, uint256 dhwIndex) external view returns (uint256 shares); /** * @notice Gets timestamps when do-hard-works were performed. * @param strategies Strategies. * @param dhwIndexes Do-hard-work indexes. * @return timestamps Timestamp for each pair of strategies and do-hard-work indexes. */ function dhwTimestamps(address[] calldata strategies, uint16a16 dhwIndexes) external view returns (uint256[] memory timestamps); function getDhwYield(address[] calldata strategies, uint16a16 dhwIndexes) external view returns (int256[] memory yields); /** * @notice Returns state of strategies at do-hard-work indexes. * @param strategies Strategies. * @param dhwIndexes Do-hard-work indexes. * @return states State of each strategy at corresponding do-hard-work index. */ function strategyAtIndexBatch(address[] calldata strategies, uint16a16 dhwIndexes, uint256 assetGroupLength) external view returns (StrategyAtIndex[] memory states); /** * @notice Gets required asset ratio for strategy at last DHW. * @param strategy Address of the strategy. * @return assetRatio Asset ratio. */ function assetRatioAtLastDhw(address strategy) external view returns (uint256[] memory assetRatio); /** * @notice Gets set platform fees. * @return fees Set platform fees. */ function platformFees() external view returns (PlatformFees memory fees); /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /** * @notice Registers a strategy into the system. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param strategy Address of strategy to register. * @param apy Apy of the strategy at the time of the registration. */ function registerStrategy(address strategy, int256 apy) external; /** * @notice Removes strategy from the system. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param strategy Strategy to remove. */ function removeStrategy(address strategy) external; /** * @notice Sets ecosystem fee. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param ecosystemFeePct Ecosystem fee to set. Expressed in terms of FULL_PERCENT. */ function setEcosystemFee(uint96 ecosystemFeePct) external; /** * @notice Sets receiver of the ecosystem fees. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param ecosystemFeeReceiver Receiver to set. */ function setEcosystemFeeReceiver(address ecosystemFeeReceiver) external; /** * @notice Sets treasury fee. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param treasuryFeePct Treasury fee to set. Expressed in terms of FULL_PERCENT. */ function setTreasuryFee(uint96 treasuryFeePct) external; /** * @notice Sets treasury fee receiver. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param treasuryFeeReceiver Receiver to set. */ function setTreasuryFeeReceiver(address treasuryFeeReceiver) external; /** * @notice Does hard work on multiple strategies. * @dev Requirements: * - caller must have role ROLE_DO_HARD_WORKER * @param dhwParams Parameters for do hard work. */ function doHardWork(DoHardWorkParameterBag calldata dhwParams) external; /** * @notice Adds deposits to strategies to be processed at next do-hard-work. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param strategies Strategies to which to add deposit. * @param amounts Amounts of assets to add to each strategy. * @return strategyIndexes Current do-hard-work indexes for the strategies. */ function addDeposits(address[] calldata strategies, uint256[][] calldata amounts) external returns (uint16a16 strategyIndexes); /** * @notice Adds withdrawals to strategies to be processed at next do-hard-work. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param strategies Strategies to which to add withdrawal. * @param strategyShares Amounts of strategy shares to add to each strategy. * @return strategyIndexes Current do-hard-work indexes for the strategies. */ function addWithdrawals(address[] calldata strategies, uint256[] calldata strategyShares) external returns (uint16a16 strategyIndexes); /** * @notice Instantly redeems strategy shares for assets. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param redeemFastParams Parameters for fast redeem. * @return withdrawnAssets Amount of assets withdrawn. */ function redeemFast(RedeemFastParameterBag calldata redeemFastParams) external returns (uint256[] memory withdrawnAssets); /** * @notice Claims withdrawals from the strategies. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * - DHWs must be run for withdrawal indexes. * @param strategies Addresses if strategies from which to claim withdrawal. * @param dhwIndexes Indexes of strategies when withdrawal was made. * @param strategyShares Amount of strategy shares that was withdrawn. * @return assetsWithdrawn Amount of assets withdrawn from strategies. */ function claimWithdrawals(address[] calldata strategies, uint16a16 dhwIndexes, uint256[] calldata strategyShares) external returns (uint256[] memory assetsWithdrawn); /** * @notice Redeems strategy shares. * Used by recipients of platform fees. * @param strategies Strategies from which to redeem. * @param shares Amount of shares to redeem from each strategy. * @param withdrawalSlippages Slippages to guard redeemal process. */ function redeemStrategyShares( address[] calldata strategies, uint256[] calldata shares, uint256[][] calldata withdrawalSlippages ) external; /** * @notice Strategy was registered * @param strategy Strategy address */ event StrategyRegistered(address indexed strategy); /** * @notice Strategy was removed * @param strategy Strategy address */ event StrategyRemoved(address indexed strategy); /** * @notice Strategy DHW was executed * @param strategy Strategy address * @param dhwIndex DHW index * @param dhwInfo DHW info */ event StrategyDhw(address indexed strategy, uint256 dhwIndex, DhwInfo dhwInfo); /** * @notice Ecosystem fee configuration was changed * @param feePct Fee percentage value */ event EcosystemFeeSet(uint256 feePct); /** * @notice Ecosystem fee receiver was changed * @param ecosystemFeeReceiver Receiver address */ event EcosystemFeeReceiverSet(address indexed ecosystemFeeReceiver); /** * @notice Treasury fee configuration was changed * @param feePct Fee percentage value */ event TreasuryFeeSet(uint256 feePct); /** * @notice Treasury fee receiver was changed * @param treasuryFeeReceiver Receiver address */ event TreasuryFeeReceiverSet(address indexed treasuryFeeReceiver); /** * @notice Emergency withdrawal wallet changed * @param wallet Emergency withdrawal wallet address */ event EmergencyWithdrawalWalletSet(address indexed wallet); /** * @notice Strategy shares have been redeemed * @param strategy Strategy address * @param owner Address that owns the shares * @param recipient Address that received the withdrawn funds * @param shares Amount of shares that were redeemed * @param assetsWithdrawn Amounts of withdrawn assets */ event StrategySharesRedeemed( address indexed strategy, address indexed owner, address indexed recipient, uint256 shares, uint256[] assetsWithdrawn ); /** * @notice Strategy shares were fast redeemed * @param strategy Strategy address * @param shares Amount of shares redeemed * @param assetsWithdrawn Amounts of withdrawn assets */ event StrategySharesFastRedeemed(address indexed strategy, uint256 shares, uint256[] assetsWithdrawn); /** * @notice Strategy APY value was updated * @param strategy Strategy address * @param apy New APY value */ event StrategyApyUpdated(address indexed strategy, int256 apy); } interface IEmergencyWithdrawal { /** * @notice Emitted when a strategy is emergency withdrawn from. * @param strategy Strategy that was emergency withdrawn from. */ event StrategyEmergencyWithdrawn(address indexed strategy); /** * @notice Set a new address that will receive assets withdrawn if emergency withdrawal is executed. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * @param wallet Address to set as the emergency withdrawal wallet. */ function setEmergencyWithdrawalWallet(address wallet) external; /** * @notice Instantly withdraws assets from a strategy, bypassing shares mechanism. * @dev Requirements: * - caller must have role ROLE_EMERGENCY_WITHDRAWAL_EXECUTOR * @param strategies Addresses of strategies. * @param withdrawalSlippages Slippages to guard withdrawal. * @param removeStrategies Whether to remove strategies from the system after withdrawal. */ function emergencyWithdraw( address[] calldata strategies, uint256[][] calldata withdrawalSlippages, bool removeStrategies ) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /** * @notice Used when an array has invalid length. */ error InvalidArrayLength(); /** * @notice Used when group of smart vaults or strategies do not have same asset group. */ error NotSameAssetGroup(); /** * @notice Used when configuring an address with a zero address. */ error ConfigurationAddressZero(); /** * @notice Used when constructor or intializer parameters are invalid. */ error InvalidConfiguration(); /** * @notice Used when fetched exchange rate is out of slippage range. */ error ExchangeRateOutOfSlippages(); /** * @notice Used when an invalid strategy is provided. * @param address_ Address of the invalid strategy. */ error InvalidStrategy(address address_); /** * @notice Used when doing low-level call on an address that is not a contract. * @param address_ Address of the contract */ error AddressNotContract(address address_); /** * @notice Used when invoking an only view execution and tx.origin is not address zero. * @param address_ Address of the tx.origin */ error OnlyViewExecution(address address_);
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "../interfaces/ISpoolAccessControl.sol"; import "../interfaces/CommonErrors.sol"; import "./Roles.sol"; /** * @notice Account access role verification middleware */ abstract contract SpoolAccessControllable { /* ========== CONSTANTS ========== */ /** * @dev Spool access control manager. */ ISpoolAccessControl internal immutable _accessControl; /* ========== CONSTRUCTOR ========== */ /** * @param accessControl_ Spool access control manager. */ constructor(ISpoolAccessControl accessControl_) { if (address(accessControl_) == address(0)) revert ConfigurationAddressZero(); _accessControl = accessControl_; } /* ========== INTERNAL FUNCTIONS ========== */ /** * @dev Reverts if an account is missing a role.\ * @param role Role to check for. * @param account Account to check. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!_accessControl.hasRole(role, account)) { revert MissingRole(role, account); } } /** * @dev Revert if an account is missing a role for a smartVault. * @param smartVault Address of the smart vault. * @param role Role to check for. * @param account Account to check. */ function _checkSmartVaultRole(address smartVault, bytes32 role, address account) internal view { if (!_accessControl.hasSmartVaultRole(smartVault, role, account)) { revert MissingRole(role, account); } } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (_accessControl.paused()) { revert SystemPaused(); } } function _checkNonReentrant() internal view { _accessControl.checkNonReentrant(); } function _nonReentrantBefore() internal { _accessControl.nonReentrantBefore(); } function _nonReentrantAfter() internal { _accessControl.nonReentrantAfter(); } /* ========== MODIFIERS ========== */ /** * @notice Only allows accounts with granted role. * @dev Reverts when the account fails check. * @param role Role to check for. * @param account Account to check. */ modifier onlyRole(bytes32 role, address account) { _checkRole(role, account); _; } /** * @notice Only allows accounts with granted role for a smart vault. * @dev Reverts when the account fails check. * @param smartVault Address of the smart vault. * @param role Role to check for. * @param account Account to check. */ modifier onlySmartVaultRole(address smartVault, bytes32 role, address account) { _checkSmartVaultRole(smartVault, role, account); _; } /** * @notice Only allows accounts that are Spool admins or admins of a smart vault. * @dev Reverts when the account fails check. * @param smartVault Address of the smart vault. * @param account Account to check. */ modifier onlyAdminOrVaultAdmin(address smartVault, address account) { _accessControl.checkIsAdminOrVaultAdmin(smartVault, account); _; } /** * @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 Prevents a contract from calling itself, or other contracts using this modifier. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } /** * @dev Check if a system has already entered in the non-reentrant state. */ modifier checkNonReentrant() { _checkNonReentrant(); _; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IWETH9 { function deposit() external payable; function withdraw(uint256 wad) external; function transfer(address dst, uint256 wad) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @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 v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Internal function that returns the initialized version. Returns `_initialized` */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Internal function that returns the initialized version. Returns `_initializing` */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /* ========== STRUCTS ========== */ /** * @notice Information needed to make a swap of assets. * @custom:member swapTarget Contract executing the swap. * @custom:member token Token to be swapped. * @custom:member swapCallData Calldata describing the swap itself. */ struct SwapInfo { address swapTarget; address token; bytes swapCallData; } /* ========== ERRORS ========== */ /** * @notice Used when trying to do a swap via an exchange that is not allowed to execute a swap. * @param exchange Exchange used. */ error ExchangeNotAllowed(address exchange); /** * @notice Used when trying to execute a swap but are not authorized. * @param caller Caller of the swap method. */ error NotSwapper(address caller); /* ========== INTERFACES ========== */ interface ISwapper { /* ========== EVENTS ========== */ /** * @notice Emitted when the exchange allowlist is updated. * @param exchange Exchange that was updated. * @param isAllowed Whether the exchange is allowed to be used in a swap or not after the update. */ event ExchangeAllowlistUpdated(address indexed exchange, bool isAllowed); event Swapped( address indexed receiver, address[] tokensIn, address[] tokensOut, uint256[] amountsIn, uint256[] amountsOut ); /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /** * @notice Performs a swap of tokens with external contracts. * - deposit tokens into the swapper contract * - swapper will swap tokens based on swap info provided * - swapper will return unswapped tokens to the receiver * @param tokensIn Addresses of tokens available for the swap. * @param swapInfo Information needed to perform the swap. * @param tokensOut Addresses of tokens to swap to. * @param receiver Receiver of unswapped tokens. * @return amountsOut Amounts of `tokensOut` sent from the swapper to the receiver. */ function swap( address[] calldata tokensIn, SwapInfo[] calldata swapInfo, address[] calldata tokensOut, address receiver ) external returns (uint256[] memory amountsOut); /** * @notice Updates list of exchanges that can be used in a swap. * @dev Requirements: * - can only be called by user granted ROLE_SPOOL_ADMIN * - exchanges and allowed arrays need to be of same length * @param exchanges Addresses of exchanges. * @param allowed Whether an exchange is allowed to be used in a swap. */ function updateExchangeAllowlist(address[] calldata exchanges, bool[] calldata allowed) external; /* ========== EXTERNAL VIEW FUNCTIONS ========== */ /** * @notice Checks if an exchange is allowed to be used in a swap. * @param exchange Exchange to check. * @return isAllowed True if the exchange is allowed to be used in a swap, false otherwise. */ function isExchangeAllowed(address exchange) external view returns (bool isAllowed); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /// @dev Number of decimals used for USD values. uint256 constant USD_DECIMALS = 18; /** * @notice Emitted when asset is invalid. * @param asset Invalid asset. */ error InvalidAsset(address asset); /** * @notice Emitted when price returned by price aggregator is negative or zero. * @param price Actual price returned by price aggregator. */ error NonPositivePrice(int256 price); /** * @notice Emitted when pricing data returned by price aggregator is not from the current * round or the round hasn't finished. */ error StalePriceData(); interface IUsdPriceFeedManager { /** * @notice Gets number of decimals for an asset. * @param asset Address of the asset. * @return assetDecimals Number of decimals for the asset. */ function assetDecimals(address asset) external view returns (uint256 assetDecimals); /** * @notice Gets number of decimals for USD. * @return usdDecimals Number of decimals for USD. */ function usdDecimals() external view returns (uint256 usdDecimals); /** * @notice Calculates asset value in USD using current price. * @param asset Address of asset. * @param assetAmount Amount of asset in asset decimals. * @return usdValue Value in USD in USD decimals. */ function assetToUsd(address asset, uint256 assetAmount) external view returns (uint256 usdValue); /** * @notice Calculates USD value in asset using current price. * @param asset Address of asset. * @param usdAmount Amount of USD in USD decimals. * @return assetValue Value in asset in asset decimals. */ function usdToAsset(address asset, uint256 usdAmount) external view returns (uint256 assetValue); /** * @notice Calculates asset value in USD using provided price. * @param asset Address of asset. * @param assetAmount Amount of asset in asset decimals. * @param price Price of asset in USD. * @return usdValue Value in USD in USD decimals. */ function assetToUsdCustomPrice(address asset, uint256 assetAmount, uint256 price) external view returns (uint256 usdValue); /** * @notice Calculates assets value in USD using provided prices. * @param assets Addresses of assets. * @param assetAmounts Amounts of assets in asset decimals. * @param prices Prices of asset in USD. * @return usdValue Value in USD in USD decimals. */ function assetToUsdCustomPriceBulk( address[] calldata assets, uint256[] calldata assetAmounts, uint256[] calldata prices ) external view returns (uint256 usdValue); /** * @notice Calculates USD value in asset using provided price. * @param asset Address of asset. * @param usdAmount Amount of USD in USD decimals. * @param price Price of asset in USD. * @return assetValue Value in asset in asset decimals. */ function usdToAssetCustomPrice(address asset, uint256 usdAmount, uint256 price) external view returns (uint256 assetValue); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; type uint16a16 is uint256; /** * @notice This library enables packing of sixteen uint16 elements into one uint256 word. */ library uint16a16Lib { /// @notice Number of bits per stored element. uint256 constant bits = 16; /// @notice Maximal number of elements stored. uint256 constant elements = 16; // must ensure that bits * elements <= 256 /// @notice Range covered by stored element. uint256 constant range = 1 << bits; /// @notice Maximal value of stored element. uint256 constant max = range - 1; /** * @notice Gets element from packed array. * @param va Packed array. * @param index Index of element to get. * @return element Element of va stored in index index. */ function get(uint16a16 va, uint256 index) internal pure returns (uint256) { require(index < elements); return (uint16a16.unwrap(va) >> (bits * index)) & max; } /** * @notice Sets element to packed array. * @param va Packed array. * @param index Index under which to store the element * @param ev Element to store. * @return va Packed array with stored element. */ function set(uint16a16 va, uint256 index, uint256 ev) internal pure returns (uint16a16) { require(index < elements); require(ev < range); index *= bits; return uint16a16.wrap((uint16a16.unwrap(va) & ~(max << index)) | (ev << index)); } /** * @notice Sets elements to packed array. * Elements are stored continuously from index 0 onwards. * @param va Packed array. * @param ev Elements to store. * @return va Packed array with stored elements. */ function set(uint16a16 va, uint256[] memory ev) internal pure returns (uint16a16) { for (uint256 i; i < ev.length; ++i) { va = set(va, i, ev[i]); } return va; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin-upgradeable/access/IAccessControlUpgradeable.sol"; /** * @notice Used when an account is missing a required role. * @param role Required role. * @param account Account missing the required role. */ error MissingRole(bytes32 role, address account); /** * @notice Used when interacting with Spool when the system is paused. */ error SystemPaused(); /** * @notice Used when setting smart vault owner */ error SmartVaultOwnerAlreadySet(address smartVault); /** * @notice Used when a contract tries to enter in a non-reentrant state. */ error ReentrantCall(); /** * @notice Used when a contract tries to call in a non-reentrant function and doesn't have the correct role. */ error NoReentrantRole(); /** * @notice thrown if unauthorized account tries to perform ownership transfer */ error OwnableUnauthorizedAccount(address account); interface ISpoolAccessControl is IAccessControlUpgradeable { /* ========== VIEW FUNCTIONS ========== */ /** * @notice Gets owner of a smart vault. * @param smartVault Smart vault. * @return owner Owner of the smart vault. */ function smartVaultOwner(address smartVault) external view returns (address owner); /** * @notice Looks if an account has a role for a smart vault. * @param smartVault Address of the smart vault. * @param role Role to look for. * @param account Account to check. * @return hasRole True if account has the role for the smart vault, false otherwise. */ function hasSmartVaultRole(address smartVault, bytes32 role, address account) external view returns (bool hasRole); /** * @notice Checks if an account is either Spool admin or admin for a smart vault. * @dev The function reverts if account is neither. * @param smartVault Address of the smart vault. * @param account to check. */ function checkIsAdminOrVaultAdmin(address smartVault, address account) external view; /** * @notice Checks if system is paused or not. * @return isPaused True if system is paused, false otherwise. */ function paused() external view returns (bool isPaused); /* ========== MUTATIVE FUNCTIONS ========== */ /** * @notice Pauses the whole system. * @dev Requirements: * - caller must have role ROLE_PAUSER */ function pause() external; /** * @notice Unpauses the whole system. * @dev Requirements: * - caller must have role ROLE_UNPAUSER */ function unpause() external; /** * @notice Grants role to an account for a smart vault. * @dev Requirements: * - caller must have either role ROLE_SPOOL_ADMIN or role ROLE_SMART_VAULT_ADMIN for the smart vault * @param smartVault Address of the smart vault. * @param role Role to grant. * @param account Account to grant the role to. */ function grantSmartVaultRole(address smartVault, bytes32 role, address account) external; /** * @notice Revokes role from an account for a smart vault. * @dev Requirements: * - caller must have either role ROLE_SPOOL_ADMIN or role ROLE_SMART_VAULT_ADMIN for the smart vault * @param smartVault Address of the smart vault. * @param role Role to revoke. * @param account Account to revoke the role from. */ function revokeSmartVaultRole(address smartVault, bytes32 role, address account) external; /** * @notice Renounce role for a smart vault. * @param smartVault Address of the smart vault. * @param role Role to renounce. */ function renounceSmartVaultRole(address smartVault, bytes32 role) external; /** * @notice Grant ownership to smart vault and assigns admin role. * @dev Ownership can only be granted once and it should be done at vault creation time. * @param smartVault Address of the smart vault. * @param owner address to which grant ownership to */ function grantSmartVaultOwnership(address smartVault, address owner) external; /** * @notice Checks and reverts if a system has already entered in the non-reentrant state. */ function checkNonReentrant() external view; /** * @notice Sets the entered flag to true when entering for the first time. * @dev Reverts if a system has already entered before. */ function nonReentrantBefore() external; /** * @notice Resets the entered flag after the call is finished. */ function nonReentrantAfter() external; /** * @notice Emitted when ownership of a smart vault is granted to an address * @param smartVault Smart vault address * @param address_ Address of the new smart vault owner */ event SmartVaultOwnershipGranted(address indexed smartVault, address indexed address_); /** * @notice Smart vault specific role was granted * @param smartVault Smart vault address * @param role Role ID * @param account Account to which the role was granted */ event SmartVaultRoleGranted(address indexed smartVault, bytes32 indexed role, address indexed account); /** * @notice Smart vault specific role was revoked * @param smartVault Smart vault address * @param role Role ID * @param account Account for which the role was revoked */ event SmartVaultRoleRevoked(address indexed smartVault, bytes32 indexed role, address indexed account); /** * @notice Smart vault specific role was renounced * @param smartVault Smart vault address * @param role Role ID * @param account Account that renounced the role */ event SmartVaultRoleRenounced(address indexed smartVault, bytes32 indexed role, address indexed account); /** * @notice SmartVault owner initiated transfer * @param previousOwner address * @param newOwner address */ event SmartVaultOwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @notice Ownership transfer is finalized * @param previousOwner address * @param newOwner address */ event SmartVaultOwnershipTransferred(address indexed previousOwner, address indexed newOwner); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; /** * @dev Grants permission to: * - acts as a default admin for other roles, * - can whitelist an action with action manager, * - can manage asset group registry. * * Is granted to the deployer of the SpoolAccessControl contract. * * Equals to the DEFAULT_ADMIN_ROLE of the OpenZeppelin AccessControl. */ bytes32 constant ROLE_SPOOL_ADMIN = 0x00; /** * @dev Grants permission to integrate a new smart vault into the Spool ecosystem. * * Should be granted to smart vault factory contracts. */ bytes32 constant ROLE_SMART_VAULT_INTEGRATOR = keccak256("SMART_VAULT_INTEGRATOR"); /** * @dev Grants permission to * - manage rewards on smart vaults, * - manage roles on smart vaults, * - redeem for another user of a smart vault. */ bytes32 constant ROLE_SMART_VAULT_ADMIN = keccak256("SMART_VAULT_ADMIN"); /** * @dev Grants permission to manage allowlists with AllowlistGuard for a smart vault. * * Should be granted to whoever is in charge of maintaining allowlists with AllowlistGuard for a smart vault. */ bytes32 constant ROLE_GUARD_ALLOWLIST_MANAGER = keccak256("GUARD_ALLOWLIST_MANAGER"); /** * @dev Grants permission to manage assets on master wallet. * * Should be granted to: * - the SmartVaultManager contract, * - the StrategyRegistry contract, * - the DepositManager contract, * - the WithdrawalManager contract. */ bytes32 constant ROLE_MASTER_WALLET_MANAGER = keccak256("MASTER_WALLET_MANAGER"); /** * @dev Marks a contract as a smart vault manager. * * Should be granted to: * - the SmartVaultManager contract, * - the DepositManager contract. */ bytes32 constant ROLE_SMART_VAULT_MANAGER = keccak256("SMART_VAULT_MANAGER"); /** * @dev Marks a contract as a strategy registry. * * Should be granted to the StrategyRegistry contract. */ bytes32 constant ROLE_STRATEGY_REGISTRY = keccak256("STRATEGY_REGISTRY"); /** * @dev Grants permission to act as a risk provider. * * Should be granted to whoever is allowed to provide risk scores. */ bytes32 constant ROLE_RISK_PROVIDER = keccak256("RISK_PROVIDER"); /** * @dev Grants permission to act as an allocation provider. * * Should be granted to contracts that are allowed to calculate allocations. */ bytes32 constant ROLE_ALLOCATION_PROVIDER = keccak256("ALLOCATION_PROVIDER"); /** * @dev Grants permission to pause the system. */ bytes32 constant ROLE_PAUSER = keccak256("SYSTEM_PAUSER"); /** * @dev Grants permission to unpause the system. */ bytes32 constant ROLE_UNPAUSER = keccak256("SYSTEM_UNPAUSER"); /** * @dev Grants permission to manage rewards payment pool. */ bytes32 constant ROLE_REWARD_POOL_ADMIN = keccak256("REWARD_POOL_ADMIN"); /** * @dev Grants permission to reallocate smart vaults. */ bytes32 constant ROLE_REALLOCATOR = keccak256("REALLOCATOR"); /** * @dev Grants permission to be used as a strategy. */ bytes32 constant ROLE_STRATEGY = keccak256("STRATEGY"); /** * @dev Grants permission to manually set strategy apy. */ bytes32 constant ROLE_STRATEGY_APY_SETTER = keccak256("STRATEGY_APY_SETTER"); /** * @dev Grants permission to manage role ROLE_STRATEGY. */ bytes32 constant ADMIN_ROLE_STRATEGY = keccak256("ADMIN_STRATEGY"); /** * @dev Grants permission vault admins to allow redeem on behalf of other users. */ bytes32 constant ROLE_SMART_VAULT_ALLOW_REDEEM = keccak256("SMART_VAULT_ALLOW_REDEEM"); /** * @dev Grants permission to manage role ROLE_SMART_VAULT_ALLOW_REDEEM. */ bytes32 constant ADMIN_ROLE_SMART_VAULT_ALLOW_REDEEM = keccak256("ADMIN_SMART_VAULT_ALLOW_REDEEM"); /** * @dev Grants permission to run do hard work. */ bytes32 constant ROLE_DO_HARD_WORKER = keccak256("DO_HARD_WORKER"); /** * @dev Grants permission to immediately withdraw assets in case of emergency. */ bytes32 constant ROLE_EMERGENCY_WITHDRAWAL_EXECUTOR = keccak256("EMERGENCY_WITHDRAWAL_EXECUTOR"); /** * @dev Grants permission to swap with swapper. * * Should be granted to the DepositSwap contract. */ bytes32 constant ROLE_SWAPPER = keccak256("SWAPPER");
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/=lib/openzeppelin-contracts/contracts/", "@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@solmate/=lib/solmate/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 99999 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": { "script/helper/ArraysHelper.sol": { "ArraysHelper": "0x7dF191D01252D2C0BABf77c725c63D69fe6EAA1a" }, "src/libraries/ArrayMapping.sol": { "ArrayMappingUint256": "0xB6B08a9ed9282c695ddFbfA81d9ebF208b4c1De0" }, "src/libraries/ReallocationLib.sol": { "ReallocationLib": "0x49CE6640EB2D89F80395C1a3695cc3a01b5C7bc1" }, "src/libraries/SpoolUtils.sol": { "SpoolUtils": "0xCB3fECd1D5eabB1b18383604A28352c17Bdfef89" }, "src/strategies/libraries/EthFrxEthAssetGroupAdapter.sol": { "EthFrxEthAssetGroupAdapter": "0xE23681bAA697EF5054E2D8C397ae59145027625D" }, "src/strategies/libraries/EthStEthAssetGroupAdapter.sol": { "EthStEthAssetGroupAdapter": "0x1516eb3a54377168b6b184326200f4258C3529dF" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IAssetGroupRegistry","name":"assetGroupRegistry_","type":"address"},{"internalType":"contract ISpoolAccessControl","name":"accessControl_","type":"address"},{"internalType":"uint256","name":"assetGroupId_","type":"uint256"},{"internalType":"contract IRocketSwapRouter","name":"rocketSwapRouter_","type":"address"},{"internalType":"address","name":"weth_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ConfigurationAddressZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"assetGroupId","type":"uint256"}],"name":"InvalidAssetGroup","type":"error"},{"inputs":[],"name":"InvalidAssetGroupIdInitialization","type":"error"},{"inputs":[],"name":"InvalidConfiguration","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"MissingRole","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"NotFastRedeemer","type":"error"},{"inputs":[],"name":"REthHoldingBeforeDepositCheckFailed","type":"error"},{"inputs":[],"name":"REthHoldingBeforeRedeemalCheckFailed","type":"error"},{"inputs":[],"name":"REthHoldingDepositSlippagesFailed","type":"error"},{"inputs":[],"name":"REthHoldingRedeemSlippagesFailed","type":"error"},{"inputs":[],"name":"StrategyWorthIsZero","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":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"BeforeDepositCheckSlippages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ssts","type":"uint256"}],"name":"BeforeRedeemalCheckSlippages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"mintedShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdWorthDeposited","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assetsBeforeSwap","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"assetsDeposited","type":"uint256[]"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"sharesMinted","type":"uint256"}],"name":"PlatformFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isDeposit","type":"bool"},{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Slippages","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":"uint256","name":"withdrawnShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdWorthWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"withdrawnAssets","type":"uint256[]"}],"name":"Withdrawn","type":"event"},{"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":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assetGroupId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetRatio","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"assets","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":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"beforeDepositCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ssts","type":"uint256"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"beforeRedeemalCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"swapInfo","type":"tuple[]"}],"name":"depositFast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"swapInfo","type":"tuple[]"},{"components":[{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct SwapInfo[]","name":"compoundSwapInfo","type":"tuple[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"uint256","name":"withdrawnShares","type":"uint256"},{"internalType":"address","name":"masterWallet","type":"address"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"},{"internalType":"int256","name":"baseYield","type":"int256"},{"components":[{"internalType":"address","name":"ecosystemFeeReceiver","type":"address"},{"internalType":"uint96","name":"ecosystemFeePct","type":"uint96"},{"internalType":"address","name":"treasuryFeeReceiver","type":"address"},{"internalType":"uint96","name":"treasuryFeePct","type":"uint96"}],"internalType":"struct PlatformFees","name":"platformFees","type":"tuple"}],"internalType":"struct StrategyDhwParameterBag","name":"dhwParams","type":"tuple"}],"name":"doHardWork","outputs":[{"components":[{"internalType":"uint256","name":"sharesMinted","type":"uint256"},{"internalType":"uint256[]","name":"assetsWithdrawn","type":"uint256[]"},{"internalType":"int256","name":"yieldPercentage","type":"int256"},{"internalType":"uint256","name":"valueAtDhw","type":"uint256"},{"internalType":"uint256","name":"totalSstsAtDhw","type":"uint256"}],"internalType":"struct DhwInfo","name":"dhwInfo","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"slippages","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getProtocolRewards","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getUnderlyingAssetAmounts","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"exchangeRates","type":"uint256[]"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager","type":"address"}],"name":"getUsdWorth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"strategyName_","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rEthToken","outputs":[{"internalType":"contract IREthToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"masterWallet","type":"address"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"redeemFast","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"redeemer","type":"address"},{"internalType":"address[]","name":"assetGroup","type":"address[]"},{"internalType":"uint256[]","name":"slippages","type":"uint256[]"}],"name":"redeemShares","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"releaseShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rocketSwapRouter","outputs":[{"internalType":"contract IRocketSwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"amount","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":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101406040523480156200001257600080fd5b50604051620060b0380380620060b0833981016040819052620000359162000194565b80858585816001600160a01b038116620000625760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b039081166080528316620000905760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0392831660a05260c052508116620000c25760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0390811660e0528216620000f05760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038216610100819052604080516332a2a83960e21b8152905163ca8aa0e4916004808201926020929091908290030181865afa1580156200013c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000162919062000208565b6001600160a01b031661012052506200022f9350505050565b6001600160a01b03811681146200019157600080fd5b50565b600080600080600060a08688031215620001ad57600080fd5b8551620001ba816200017b565b6020870151909550620001cd816200017b565b604087015160608801519195509350620001e7816200017b565b6080870151909250620001fa816200017b565b809150509295509295909350565b6000602082840312156200021b57600080fd5b815162000228816200017b565b9392505050565b60805160a05160c05160e0516101005161012051615d89620003276000396000818161027701528181611c4c01528181612b4401528181612ce3015281816132e80152818161392001528181613c3a0152818161430701526144e7015260008181610595015281816139f701528181613c5b01528181613c8201528181614091015261438b01526000818161036b0152818161214e01528181612c0701528181612dd5015281816148ae015261495f015260008181610a5701528181610a8501526137350152600081816109440152818161207b01526137d30152600081816122c30152818161239f01526129e70152615d896000f3fe6080604052600436106101dc5760003560e01c806395d89b4111610102578063c1a7d80e11610095578063e173ad2511610064578063e173ad251461060a578063ebbd6bc71461061f578063f62d18881461063f578063fc195d8c1461065f57600080fd5b8063c1a7d80e14610543578063c8d31e7414610563578063d5ddd50314610583578063dd62ed3e146105b757600080fd5b8063a63e8c4b116100d1578063a63e8c4b146104cc578063a9059cbb146104e1578063beef8a7114610501578063c1a0ff4c1461052e57600080fd5b806395d89b41146104545780639b6aaa4714610469578063a3ea6c971461048c578063a457c2d7146104ac57600080fd5b8063313ce5671161017a57806370a082311161014957806370a08231146103ad57806371a97305146103f05780637817bf4a1461041257806384ba89e31461043f57600080fd5b8063313ce5671461031d57806339509351146103395780633fc8cef3146103595780636c60d9e71461038d57600080fd5b806317094411116101b6578063170944111461026557806318160ddd146102be57806323b872dd146102dd5780632ce5183f146102fd57600080fd5b806306fdde03146101e8578063095ea7b3146102135780631480fce31461024357600080fd5b366101e357005b600080fd5b3480156101f457600080fd5b506101fd61067f565b60405161020a9190614f3f565b60405180910390f35b34801561021f57600080fd5b5061023361022e366004614f77565b610711565b604051901515815260200161020a565b34801561024f57600080fd5b5061026361025e366004614fef565b61072b565b005b34801561027157600080fd5b506102997f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020a565b3480156102ca57600080fd5b506035545b60405190815260200161020a565b3480156102e957600080fd5b506102336102f836600461503b565b610859565b34801561030957600080fd5b50610263610318366004614f77565b61087f565b34801561032957600080fd5b506040516012815260200161020a565b34801561034557600080fd5b50610233610354366004614f77565b6108b6565b34801561036557600080fd5b506102997f000000000000000000000000000000000000000000000000000000000000000081565b34801561039957600080fd5b506102636103a836600461507c565b610902565b3480156103b957600080fd5b506102cf6103c83660046150d3565b73ffffffffffffffffffffffffffffffffffffffff1660009081526033602052604090205490565b3480156103fc57600080fd5b50610405610940565b60405161020a9190615141565b34801561041e57600080fd5b5061043261042d366004615154565b610a0c565b60405161020a9190615210565b34801561044b57600080fd5b506102cf610a52565b34801561046057600080fd5b506101fd610aa7565b34801561047557600080fd5b5061047e610ab6565b60405161020a929190615223565b34801561049857600080fd5b506102cf6104a7366004615251565b610acd565b3480156104b857600080fd5b506102336104c7366004614f77565b610ea9565b3480156104d857600080fd5b50610432610f8a565b3480156104ed57600080fd5b506102336104fc366004614f77565b610fd5565b34801561050d57600080fd5b5061052161051c36600461532c565b610fe3565b60405161020a9190615368565b34801561053a57600080fd5b50610432611bfb565b34801561054f57600080fd5b5061026361055e3660046154c5565b611d55565b34801561056f57600080fd5b506102cf61057e366004615521565b611ebf565b34801561058f57600080fd5b506102997f000000000000000000000000000000000000000000000000000000000000000081565b3480156105c357600080fd5b506102cf6105d2366004615573565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260346020908152604080832093909416825291909152205490565b34801561061657600080fd5b506101fd611f02565b34801561062b57600080fd5b5061026361063a366004614f77565b611f11565b34801561064b57600080fd5b5061026361065a3660046155a1565b611f48565b34801561066b57600080fd5b5061043261067a366004615154565b61226c565b60606036805461068e90615654565b80601f01602080910402602001604051908101604052809291908181526020018280546106ba90615654565b80156107075780601f106106dc57610100808354040283529160200191610707565b820191906000526020600020905b8154815290600101906020018083116106ea57829003601f168201915b5050505050905090565b60003361071f818585612469565b60019150505b92915050565b32610768576040518381527f0d4fc85ef490d33e67589b22099d3954a460f3ab83b44caad5335d7f2f2c635a9060200160405180910390a1505050565b600060028383600081811061077f5761077f6156a7565b9050602002013510156107ad578282600281811061079f5761079f6156a7565b905060200201359050610813565b828260008181106107c0576107c06156a7565b905060200201356002036107e1578282600181811061079f5761079f6156a7565b6040517fb23013d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61081d818561261c565b610853576040517fb23013d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b600033610867858285612654565b610872858585612725565b60019150505b9392505050565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa56336108ab828261299b565b610853308585612725565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919061071f90829086906108fd908790615705565b612469565b7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361092e828261299b565b610939858585612aa7565b5050505050565b60607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a610986610a52565b6040518263ffffffff1660e01b81526004016109a491815260200190565b600060405180830381865afa1580156109c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a079190810190615718565b905090565b6060610a387fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361299b565b610a4787878888888888612c2e565b979650505050505050565b6000807f000000000000000000000000000000000000000000000000000000000000000011610a82575060975490565b507f000000000000000000000000000000000000000000000000000000000000000090565b60606037805461068e90615654565b6060803215610ac457600080fd5b50606091829150565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633610afb828261299b565b60008b67ffffffffffffffff811115610b1657610b166153b8565b604051908082528060200260200182016040528015610b3f578160200160208202803683370190505b50905060005b8c811015610c31578d8d82818110610b5f57610b5f6156a7565b9050602002016020810190610b7491906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0491906157a7565b828281518110610c1657610c166156a7565b6020908102919091010152610c2a816157c0565b9050610b45565b50610c6b8d8d8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050505050565b60008c67ffffffffffffffff811115610c8657610c866153b8565b604051908082528060200260200182016040528015610caf578160200160208202803683370190505b50905060005b8d811015610da1578e8e82818110610ccf57610ccf6156a7565b9050602002016020810190610ce491906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7491906157a7565b828281518110610d8657610d866156a7565b6020908102919091010152610d9a816157c0565b9050610cb5565b506000610de28d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508f9250612c9b915050565b9050610df18f8f848d8d612eb5565b6000610e3e8e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d612c9b565b90506000610e4c8383612f9d565b90507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc81610e7a85856157f8565b8787604051610e8c949392919061580b565b60405180910390a196505050505050509998505050505050505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610f72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610f7f8286868403612469565b506001949350505050565b6040805160018082528183019092526060916000919060208083019080368337019050509050600181600081518110610fc557610fc56156a7565b6020908102919091010152919050565b60003361071f818585612725565b6110156040518060a0016040528060008152602001606081526020016000815260200160008152602001600081525090565b61103f7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361299b565b60008061104f606085018561583c565b905067ffffffffffffffff811115611069576110696153b8565b604051908082528060200260200182016040528015611092578160200160208202803683370190505b50905060005b6110a5606086018661583c565b90508110156111bd576110bb606086018661583c565b828181106110cb576110cb6156a7565b90506020020160208101906110e091906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa15801561114c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117091906157a7565b828281518110611182576111826156a7565b60200260200101818152505060008282815181106111a2576111a26156a7565b602002602001015111156111b557600192505b600101611098565b506111cf8161055e604087018761583c565b6111e460a085013561025e604087018761583c565b6111ec614eb3565b6111fa85610100013561302b565b60408501526000611237611211606088018861583c565b61121e60208a018a61583c565b61122b60408c018c61583c565b60009695505050505050565b905064e8d4a5100085604001518261124f91906158a4565b611259919061591f565b6112639082615987565b856040018181516112749190615987565b90525050604084015161128b906101208701613050565b506112e461129c608087018761583c565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506112df92505050610100880160e089016150d3565b612c9b565b81526000808060a088013586156114355760006113086101008b0160e08c016150d3565b73ffffffffffffffffffffffffffffffffffffffff1663a2d2657f61133060608d018d61583c565b8a8e8060800190611341919061583c565b6040518663ffffffff1660e01b81526004016113619594939291906159a7565b602060405180830381865afa15801561137e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a291906157a7565b905064e8d4a510006113b360355490565b10156113cc576113c5816103e8615a5f565b935061141f565b8551156113ed5785516035546113e3908390615a5f565b6113c59190615a76565b6040517f907d9a5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8184101561142f57839450611433565b8194505b505b600061144460608b018b61583c565b905067ffffffffffffffff81111561145e5761145e6153b8565b604051908082528060200260200182016040528015611487578160200160208202803683370190505b5090506000828511156118d25785156115485760005b6114aa60608d018d61583c565b90508110156115425785878a83815181106114c7576114c76156a7565b602002602001015102816114dd576114dd6158f0565b048382815181106114f0576114f06156a7565b60200260200101818152505082818151811061150e5761150e6156a7565b6020026020010151898281518110611528576115286156a7565b60209081029190910101805191909103905260010161149d565b50600190505b6000885167ffffffffffffffff811115611564576115646153b8565b60405190808252806020026020018201604052801561158d578160200160208202803683370190505b509050600061159c8d8061583c565b90501115611772576116056115b460608e018e61583c565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508a8e8060000190610853919061583c565b60005b61161560608e018e61583c565b905081101561176c57898181518110611630576116306156a7565b602002602001015182828151811061164a5761164a6156a7565b602002602001018181525050838181518110611668576116686156a7565b60200260200101518d8060600190611680919061583c565b83818110611690576116906156a7565b90506020020160208101906116a591906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611711573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173591906157a7565b61173f91906157f8565b8a8281518110611751576117516156a7565b6020908102919091010152611765816157c0565b9050611608565b506117d4565b60005b61178260608e018e61583c565b90508110156117d25789818151811061179d5761179d6156a7565b60200260200101518282815181106117b7576117b76156a7565b60209081029190910101526117cb816157c0565b9050611775565b505b6117fa6117e460608e018e61583c565b8b8f80604001906117f5919061583c565b612eb5565b61185d61180a60808e018e61583c565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d60e00160208101906112df91906150d3565b60208901819052885161186f91612f9d565b885160208a01519196507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc9187916118a6916157f8565b838c6040516118b8949392919061580b565b60405180910390a16118ca8786615705565b945050611b29565b84831115611aba5785156118e95785830392508593505b61190f6118f960608d018d61583c565b858e806040019061190a919061583c565b6131ca565b61191930846133c7565b50600161196f61192c60808d018d61583c565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506112df925050506101008e0160e08f016150d3565b602088015260005b61198460608d018d61583c565b9050811015611a745761199a60608d018d61583c565b828181106119aa576119aa6156a7565b90506020020160208101906119bf91906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611a2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4f91906157a7565b838281518110611a6157611a616156a7565b6020908102919091010152600101611977565b5060208701516040517f8b1b306a5c3a19617717b1f37f2b4bea82978af8db2e859975f665fc40bc74b391611aad918691908690615a8a565b60405180910390a1611b29565b8515611b215782935060005b611ad360608d018d61583c565b9050811015611b1b57888181518110611aee57611aee6156a7565b6020026020010151838281518110611b0857611b086156a7565b6020908102919091010152600101611ac6565b50600190505b865160208801525b8015611bd15760005b611b3f60608d018d61583c565b9050811015611bcf57611bc7611b5b60e08e0160c08f016150d3565b848381518110611b6d57611b6d6156a7565b60200260200101518e8060600190611b85919061583c565b85818110611b9557611b956156a7565b9050602002016020810190611baa91906150d3565b73ffffffffffffffffffffffffffffffffffffffff16919061358d565b600101611b32565b505b838a526020808b0183905287015160608b015260355460808b015250979998505050505050505050565b60408051600180825281830190925260609160208083019080368337019050506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690638b32fa239082906370a0823190602401602060405180830381865afa158015611cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd491906157a7565b6040518263ffffffff1660e01b8152600401611cf291815260200190565b602060405180830381865afa158015611d0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3391906157a7565b81600081518110611d4657611d466156a7565b60200260200101818152505090565b32611df3576040805160018082528183019092526000916020808301908036833701905050905083600081518110611d8f57611d8f6156a7565b602002602001015181600081518110611daa57611daa6156a7565b6020026020010181815250507f2da97ade1b5402d92aa4ecf88302c7c6a98498999fe040468284622731550b3681604051611de59190615210565b60405180910390a150505050565b600282826000818110611e0857611e086156a7565b905060200201351115611e47576040517fdfc8737800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e8482826001818110611e5d57611e5d6156a7565b9050602002013584600081518110611e7757611e776156a7565b602002602001015161261c565b611eba576040517fdfc8737800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633611eed828261299b565b611ef78585612c9b565b92505b505092915050565b60606096805461068e90615654565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633611f3d828261299b565b610853843085612725565b600054610100900460ff1615808015611f685750600054600160ff909116105b80611f825750303b158015611f82575060005460ff166001145b61200e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610f69565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561206c57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b612077826000613661565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a6120bd610a52565b6040518263ffffffff1660e01b81526004016120db91815260200190565b600060405180830381865afa1580156120f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261213e9190810190615718565b9050805160011415806121b757507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681600081518110612196576121966156a7565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614155b156121f9576121c4610a52565b6040517f4b1f57ad000000000000000000000000000000000000000000000000000000008152600401610f6991815260200190565b6122016138e7565b60985550801561226857600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5660048201523360248201526060907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa15801561231f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123439190615aa9565b15801561242157506040517f91d148540000000000000000000000000000000000000000000000000000000081527fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded260048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa1580156123fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241f9190615aa9565b155b1561245a576040517f7ab612a3000000000000000000000000000000000000000000000000000000008152336004820152602401610f69565b610a4787308888888888612c2e565b73ffffffffffffffffffffffffffffffffffffffff831661250b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff82166125ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600061263a60017001000000000000000000000000000000006157f8565b831682108061264c5750608083901c82115b159392505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152603460209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146108535781811015612718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610f69565b6108538484848403612469565b73ffffffffffffffffffffffffffffffffffffffff83166127c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff821661286b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff831660009081526033602052604090205481811015612921576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061298e9086815260200190565b60405180910390a3610853565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401602060405180830381865afa158015612a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a529190615aa9565b612268576040517f75000dc00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82166024820152604401610f69565b82826000818110612aba57612aba6156a7565b90506020020135600314612afa576040517ffc5fbdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090612beb9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612baf91906157a7565b858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250600192506139a0915050565b905061085373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838361358d565b60606000612c3f86868b8787613dd9565b9050612c4b888a6133c7565b60005b85811015612c8e57612c8688838381518110612c6c57612c6c6156a7565b6020026020010151898985818110611b9557611b956156a7565b600101612c4e565b5098975050505050505050565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690638b32fa239082906370a0823190602401602060405180830381865afa158015612d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5691906157a7565b6040518263ffffffff1660e01b8152600401612d7491815260200190565b602060405180830381865afa158015612d91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612db591906157a7565b90508273ffffffffffffffffffffffffffffffffffffffff16639115900c7f00000000000000000000000000000000000000000000000000000000000000008387600081518110612e0857612e086156a7565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff909316600484015260248301919091526044820152606401602060405180830381865afa158015612e89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ead91906157a7565b949350505050565b600082826000818110612eca57612eca6156a7565b90506020020135600003612ee057506003612f3b565b82826000818110612ef357612ef36156a7565b90506020020135600203612f0957506002612f3b565b6040517f0947e0a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9484600081518110612f5157612f516156a7565b602002602001015184848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061403a915050565b50505050505050565b600080612fa960355490565b905064e8d4a51000811015612ff157612fc46103e884615a5f565b915064e8d4a5100081900380831015612fda5750815b8083039250612feb61dead82614573565b5061301a565b83156113ed57838161300382866157f8565b61300d9190615a5f565b6130179190615a76565b91505b6130243083614573565b5092915050565b6000806130366138e7565b905061304460985482614668565b60989190915592915050565b6000808313156107255782600061306e64e8d4a51000612710615a5f565b61307e6080860160608701615acb565b6bffffffffffffffffffffffff1661309c6040870160208801615acb565b6130b6906bffffffffffffffffffffffff166127106157f8565b6130c091906157f8565b6130ca9084615a5f565b6130d49190615705565b90506000826130e260355490565b6130ec9190615a5f565b90506000826131016040880160208901615acb565b613119906bffffffffffffffffffffffff1684615a5f565b6131239190615a76565b905061313b61313560208801886150d3565b82614573565b60008361314e6080890160608a01615acb565b613166906bffffffffffffffffffffffff1685615a5f565b6131709190615a76565b90506131856131356060890160408a016150d3565b604051828201808252965030907fd02a13a1b75d55c8b9ced16f3558e9004a0827559f52715b34bcc685d904c4599060200160405180910390a2505050505092915050565b6000828260008181106131df576131df6156a7565b905060200201356001036131f5575060036132ad565b82826000818110613208576132086156a7565b9050602002013560020361321e575060026132ad565b82826000818110613231576132316156a7565b90506020020135600303613247575060016132ad565b8282600081811061325a5761325a6156a7565b90506020020135600014801561326e575032155b1561327b575060036132ad565b6040517ffc5fbdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006132b860355490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015286907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613344573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061336891906157a7565b6133729190615a5f565b61337c9190615a76565b90506133bd818585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508792506139a0915050565b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661346a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff821660009081526033602052604090205481811015613520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611eba9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526146d4565b600054610100900460ff166136f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b8151600003613733576040517fc52a9bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000613799578061378f576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60978190556137d1565b80156137d1576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663e065ce6e613815610a52565b6040518263ffffffff1660e01b815260040161383391815260200190565b60006040518083038186803b15801561384b57600080fd5b505afa15801561385f573d6000803e3d6000fd5b5050505081609690816138729190615b3f565b506122686040518060400160405280601481526020017f537472617465677920536861726520546f6b656e0000000000000000000000008152506040518060400160405280600381526020017f53535400000000000000000000000000000000000000000000000000000000008152506147e0565b6040517f8b32fa23000000000000000000000000000000000000000000000000000000008152670de0b6b3a764000060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690638b32fa2390602401602060405180830381865afa15801561397c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0791906157a7565b600032613c35576040517f2f18e2af00000000000000000000000000000000000000000000000000000000815260048101859052600a6024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632f18e2af906044016060604051808303816000875af1158015613a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a649190615c59565b90925090508160006020020151858581518110613a8357613a836156a7565b6020908102919091010152816001602002015185613aa2866001615705565b81518110613ab257613ab26156a7565b60209081029190910101528085613aca866002615705565b81518110613ada57613ada6156a7565b60209081029190910101528085613af2866003615705565b81518110613b0257613b026156a7565b602090810291909101015260408051600480825260a0820190925260009181602001602082028036833750508451825192935091839150600090613b4857613b486156a7565b6020908102919091010152826001602002015181600181518110613b6e57613b6e6156a7565b6020026020010181815250508181600281518110613b8e57613b8e6156a7565b6020026020010181815250508181600381518110613bae57613bae6156a7565b6020026020010181815250507f872697b0ffc2830e88f47a2ddac8d40ec8898cb72bc036d3aaf8ce7960286a9060008083604051602001613bef9190615210565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052613c29939291615cde565b60405180910390a15050505b613c807f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000086614881565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a824ae8b848481518110613cce57613cce6156a7565b602002602001015185856001613ce49190615705565b81518110613cf457613cf46156a7565b602002602001015186866002613d0a9190615705565b81518110613d1a57613d1a6156a7565b602002602001015187876003613d309190615705565b81518110613d4057613d406156a7565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815260048101949094526024840192909252604483015260648201526084810187905260a401600060405180830381600087803b158015613db557600080fd5b505af1158015613dc9573d6000803e3d6000fd5b50505050479050610878816148ac565b60608467ffffffffffffffff811115613df457613df46153b8565b604051908082528060200260200182016040528015613e1d578160200160208202803683370190505b50905060005b85811015613f0f57868682818110613e3d57613e3d6156a7565b9050602002016020810190613e5291906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee291906157a7565b828281518110613ef457613ef46156a7565b6020908102919091010152613f08816157c0565b9050613e23565b50613f1d86868686866131ca565b60005b8581101561403057818181518110613f3a57613f3a6156a7565b6020026020010151878783818110613f5457613f546156a7565b9050602002016020810190613f6991906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff991906157a7565b61400391906157f8565b828281518110614015576140156156a7565b6020908102919091010152614029816157c0565b9050613f20565b5095945050505050565b6000326142d0576040517f27e0416300000000000000000000000000000000000000000000000000000000815260048101859052600a6024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906327e04163906044016060604051808303816000875af11580156140da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140fe9190615c59565b9092509050816000602002015185858151811061411d5761411d6156a7565b602090810291909101015281600160200201518561413c866001615705565b8151811061414c5761414c6156a7565b60209081029190910101528085614164866002615705565b81518110614174576141746156a7565b6020908102919091010152808561418c866003615705565b8151811061419c5761419c6156a7565b602090810291909101015260408051600480825260a08201909252600091816020016020820280368337505084518251929350918391506000906141e2576141e26156a7565b6020908102919091010152826001602002015181600181518110614208576142086156a7565b6020026020010181815250508181600281518110614228576142286156a7565b6020026020010181815250508181600381518110614248576142486156a7565b6020026020010181815250507f872697b0ffc2830e88f47a2ddac8d40ec8898cb72bc036d3aaf8ce7960286a90600160008360405160200161428a9190615210565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526142c4939291615cde565b60405180910390a15050505b6142d984614930565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614363573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438791906157a7565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166355362f4d858585815181106143d8576143d86156a7565b6020026020010151868660016143ee9190615705565b815181106143fe576143fe6156a7565b6020026020010151878760026144149190615705565b81518110614424576144246156a7565b60200260200101518888600361443a9190615705565b8151811061444a5761444a6156a7565b60200260200101516040518663ffffffff1660e01b8152600401614487949392919093845260208401929092526040830152606082015260800190565b6000604051808303818588803b1580156144a057600080fd5b505af11580156144b4573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528493507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1692506370a082319150602401602060405180830381865afa158015614545573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061456991906157a7565b612ead91906157f8565b73ffffffffffffffffffffffffffffffffffffffff82166145f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610f69565b80603560008282546146029190615705565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60008282111561469e578264e8d4a5100061468382856157f8565b61468d9190615a5f565b6146979190615a76565b9050610725565b81831115610725578264e8d4a510006146b784836157f8565b6146c19190615a5f565b6146cb9190615a76565b61087890615cff565b6000614736826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166149cc9092919063ffffffff16565b805190915015611eba57808060200190518101906147549190615aa9565b611eba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610f69565b600054610100900460ff16614877576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b61226882826149db565b61488b8383614a8b565b611eba73ffffffffffffffffffffffffffffffffffffffff84168383614b49565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561491457600080fd5b505af1158015614928573d6000803e3d6000fd5b505050505050565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b1580156149b857600080fd5b505af1158015610939573d6000803e3d6000fd5b6060612ead8484600085614ccb565b600054610100900460ff16614a72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b6036614a7e8382615b3f565b506037611eba8282615b3f565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015614b01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b2591906157a7565b11156122685761226873ffffffffffffffffffffffffffffffffffffffff83168260005b801580614be957506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614bc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be791906157a7565b155b614c75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610f69565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611eba9084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016135df565b606082471015614d5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610f69565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614d869190615d37565b60006040518083038185875af1925050503d8060008114614dc3576040519150601f19603f3d011682016040523d82523d6000602084013e614dc8565b606091505b5091509150610a478783838760608315614e6a578251600003614e635773ffffffffffffffffffffffffffffffffffffffff85163b614e63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610f69565b5081612ead565b612ead8383815115614e7f5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f699190614f3f565b60405180604001604052806002906020820280368337509192915050565b60005b83811015614eec578181015183820152602001614ed4565b50506000910152565b60008151808452614f0d816020860160208601614ed1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108786020830184614ef5565b73ffffffffffffffffffffffffffffffffffffffff81168114614f7457600080fd5b50565b60008060408385031215614f8a57600080fd5b8235614f9581614f52565b946020939093013593505050565b60008083601f840112614fb557600080fd5b50813567ffffffffffffffff811115614fcd57600080fd5b6020830191508360208260051b8501011115614fe857600080fd5b9250929050565b60008060006040848603121561500457600080fd5b83359250602084013567ffffffffffffffff81111561502257600080fd5b61502e86828701614fa3565b9497909650939450505050565b60008060006060848603121561505057600080fd5b833561505b81614f52565b9250602084013561506b81614f52565b929592945050506040919091013590565b60008060006040848603121561509157600080fd5b833567ffffffffffffffff8111156150a857600080fd5b6150b486828701614fa3565b90945092505060208401356150c881614f52565b809150509250925092565b6000602082840312156150e557600080fd5b813561087881614f52565b600081518084526020808501945080840160005b8381101561513657815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101615104565b509495945050505050565b60208152600061087860208301846150f0565b6000806000806000806080878903121561516d57600080fd5b86359550602087013561517f81614f52565b9450604087013567ffffffffffffffff8082111561519c57600080fd5b6151a88a838b01614fa3565b909650945060608901359150808211156151c157600080fd5b506151ce89828a01614fa3565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015615136578151875295820195908201906001016151f4565b60208152600061087860208301846151e0565b60408152600061523660408301856150f0565b828103602084015261524881856151e0565b95945050505050565b600080600080600080600080600060a08a8c03121561526f57600080fd5b893567ffffffffffffffff8082111561528757600080fd5b6152938d838e01614fa3565b909b50995060208c01359150808211156152ac57600080fd5b6152b88d838e01614fa3565b909950975060408c013591506152cd82614f52565b90955060608b013590808211156152e357600080fd5b6152ef8d838e01614fa3565b909650945060808c013591508082111561530857600080fd5b506153158c828d01614fa3565b915080935050809150509295985092959850929598565b60006020828403121561533e57600080fd5b813567ffffffffffffffff81111561535557600080fd5b82016101a0818503121561087857600080fd5b60208152815160208201526000602083015160a0604084015261538e60c08401826151e0565b90506040840151606084015260608401516080840152608084015160a08401528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561542e5761542e6153b8565b604052919050565b600067ffffffffffffffff821115615450576154506153b8565b5060051b60200190565b600082601f83011261546b57600080fd5b8135602061548061547b83615436565b6153e7565b82815260059290921b8401810191818101908684111561549f57600080fd5b8286015b848110156154ba57803583529183019183016154a3565b509695505050505050565b6000806000604084860312156154da57600080fd5b833567ffffffffffffffff808211156154f257600080fd5b6154fe8783880161545a565b9450602086013591508082111561551457600080fd5b5061502e86828701614fa3565b6000806040838503121561553457600080fd5b823567ffffffffffffffff81111561554b57600080fd5b6155578582860161545a565b925050602083013561556881614f52565b809150509250929050565b6000806040838503121561558657600080fd5b823561559181614f52565b9150602083013561556881614f52565b600060208083850312156155b457600080fd5b823567ffffffffffffffff808211156155cc57600080fd5b818501915085601f8301126155e057600080fd5b8135818111156155f2576155f26153b8565b615622847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016153e7565b9150808252868482850101111561563857600080fd5b8084840185840137600090820190930192909252509392505050565b600181811c9082168061566857607f821691505b6020821081036156a1577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610725576107256156d6565b6000602080838503121561572b57600080fd5b825167ffffffffffffffff81111561574257600080fd5b8301601f8101851361575357600080fd5b805161576161547b82615436565b81815260059190911b8201830190838101908783111561578057600080fd5b928401925b82841015610a4757835161579881614f52565b82529284019290840190615785565b6000602082840312156157b957600080fd5b5051919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157f1576157f16156d6565b5060010190565b81810381811115610725576107256156d6565b84815283602082015260806040820152600061582a60808301856151e0565b8281036060840152610a4781856151e0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261587157600080fd5b83018035915067ffffffffffffffff82111561588c57600080fd5b6020019150600581901b3603821315614fe857600080fd5b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156158dc576158dc6156d6565b8181058314821517610725576107256156d6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261592e5761592e6158f0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615615982576159826156d6565b500590565b8082018281126000831280158216821582161715611efa57611efa6156d6565b6060808252810185905260008660808301825b888110156159f75782356159cd81614f52565b73ffffffffffffffffffffffffffffffffffffffff168252602092830192909101906001016159ba565b508381036020850152615a0a81886151e0565b91505082810360408401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115615a4557600080fd5b8360051b8086602084013701602001979650505050505050565b8082028115828204841417610725576107256156d6565b600082615a8557615a856158f0565b500490565b83815282602082015260606040820152600061524860608301846151e0565b600060208284031215615abb57600080fd5b8151801515811461087857600080fd5b600060208284031215615add57600080fd5b81356bffffffffffffffffffffffff8116811461087857600080fd5b601f821115611eba57600081815260208120601f850160051c81016020861015615b205750805b601f850160051c820191505b8181101561492857828155600101615b2c565b815167ffffffffffffffff811115615b5957615b596153b8565b615b6d81615b678454615654565b84615af9565b602080601f831160018114615bc05760008415615b8a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614928565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015615c0d57888601518255948401946001909101908401615bee565b5085821015615c4957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008060608385031215615c6c57600080fd5b83601f840112615c7b57600080fd5b6040516040810181811067ffffffffffffffff82111715615c9e57615c9e6153b8565b8060405250806040850186811115615cb557600080fd5b855b81811015615ccf578051835260209283019201615cb7565b50519196919550909350505050565b83151581528260208201526060604082015260006152486060830184614ef5565b60007f80000000000000000000000000000000000000000000000000000000000000008203615d3057615d306156d6565b5060000390565b60008251615d49818460208701614ed1565b919091019291505056fea26469706673582212205740574dd20ba806085e79746ceccc677ad2b2edf42947d6d5255ed62ba8944c64736f6c634300081100330000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd13000000000000000000000000000000000000000000000000000000000000000100000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Deployed Bytecode
0x6080604052600436106101dc5760003560e01c806395d89b4111610102578063c1a7d80e11610095578063e173ad2511610064578063e173ad251461060a578063ebbd6bc71461061f578063f62d18881461063f578063fc195d8c1461065f57600080fd5b8063c1a7d80e14610543578063c8d31e7414610563578063d5ddd50314610583578063dd62ed3e146105b757600080fd5b8063a63e8c4b116100d1578063a63e8c4b146104cc578063a9059cbb146104e1578063beef8a7114610501578063c1a0ff4c1461052e57600080fd5b806395d89b41146104545780639b6aaa4714610469578063a3ea6c971461048c578063a457c2d7146104ac57600080fd5b8063313ce5671161017a57806370a082311161014957806370a08231146103ad57806371a97305146103f05780637817bf4a1461041257806384ba89e31461043f57600080fd5b8063313ce5671461031d57806339509351146103395780633fc8cef3146103595780636c60d9e71461038d57600080fd5b806317094411116101b6578063170944111461026557806318160ddd146102be57806323b872dd146102dd5780632ce5183f146102fd57600080fd5b806306fdde03146101e8578063095ea7b3146102135780631480fce31461024357600080fd5b366101e357005b600080fd5b3480156101f457600080fd5b506101fd61067f565b60405161020a9190614f3f565b60405180910390f35b34801561021f57600080fd5b5061023361022e366004614f77565b610711565b604051901515815260200161020a565b34801561024f57600080fd5b5061026361025e366004614fef565b61072b565b005b34801561027157600080fd5b506102997f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639381565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020a565b3480156102ca57600080fd5b506035545b60405190815260200161020a565b3480156102e957600080fd5b506102336102f836600461503b565b610859565b34801561030957600080fd5b50610263610318366004614f77565b61087f565b34801561032957600080fd5b506040516012815260200161020a565b34801561034557600080fd5b50610233610354366004614f77565b6108b6565b34801561036557600080fd5b506102997f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561039957600080fd5b506102636103a836600461507c565b610902565b3480156103b957600080fd5b506102cf6103c83660046150d3565b73ffffffffffffffffffffffffffffffffffffffff1660009081526033602052604090205490565b3480156103fc57600080fd5b50610405610940565b60405161020a9190615141565b34801561041e57600080fd5b5061043261042d366004615154565b610a0c565b60405161020a9190615210565b34801561044b57600080fd5b506102cf610a52565b34801561046057600080fd5b506101fd610aa7565b34801561047557600080fd5b5061047e610ab6565b60405161020a929190615223565b34801561049857600080fd5b506102cf6104a7366004615251565b610acd565b3480156104b857600080fd5b506102336104c7366004614f77565b610ea9565b3480156104d857600080fd5b50610432610f8a565b3480156104ed57600080fd5b506102336104fc366004614f77565b610fd5565b34801561050d57600080fd5b5061052161051c36600461532c565b610fe3565b60405161020a9190615368565b34801561053a57600080fd5b50610432611bfb565b34801561054f57600080fd5b5061026361055e3660046154c5565b611d55565b34801561056f57600080fd5b506102cf61057e366004615521565b611ebf565b34801561058f57600080fd5b506102997f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c81565b3480156105c357600080fd5b506102cf6105d2366004615573565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260346020908152604080832093909416825291909152205490565b34801561061657600080fd5b506101fd611f02565b34801561062b57600080fd5b5061026361063a366004614f77565b611f11565b34801561064b57600080fd5b5061026361065a3660046155a1565b611f48565b34801561066b57600080fd5b5061043261067a366004615154565b61226c565b60606036805461068e90615654565b80601f01602080910402602001604051908101604052809291908181526020018280546106ba90615654565b80156107075780601f106106dc57610100808354040283529160200191610707565b820191906000526020600020905b8154815290600101906020018083116106ea57829003601f168201915b5050505050905090565b60003361071f818585612469565b60019150505b92915050565b32610768576040518381527f0d4fc85ef490d33e67589b22099d3954a460f3ab83b44caad5335d7f2f2c635a9060200160405180910390a1505050565b600060028383600081811061077f5761077f6156a7565b9050602002013510156107ad578282600281811061079f5761079f6156a7565b905060200201359050610813565b828260008181106107c0576107c06156a7565b905060200201356002036107e1578282600181811061079f5761079f6156a7565b6040517fb23013d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61081d818561261c565b610853576040517fb23013d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b600033610867858285612654565b610872858585612725565b60019150505b9392505050565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa56336108ab828261299b565b610853308585612725565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919061071f90829086906108fd908790615705565b612469565b7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361092e828261299b565b610939858585612aa7565b5050505050565b60607f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a610986610a52565b6040518263ffffffff1660e01b81526004016109a491815260200190565b600060405180830381865afa1580156109c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a079190810190615718565b905090565b6060610a387fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361299b565b610a4787878888888888612c2e565b979650505050505050565b6000807f000000000000000000000000000000000000000000000000000000000000000111610a82575060975490565b507f000000000000000000000000000000000000000000000000000000000000000190565b60606037805461068e90615654565b6060803215610ac457600080fd5b50606091829150565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633610afb828261299b565b60008b67ffffffffffffffff811115610b1657610b166153b8565b604051908082528060200260200182016040528015610b3f578160200160208202803683370190505b50905060005b8c811015610c31578d8d82818110610b5f57610b5f6156a7565b9050602002016020810190610b7491906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0491906157a7565b828281518110610c1657610c166156a7565b6020908102919091010152610c2a816157c0565b9050610b45565b50610c6b8d8d8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050505050565b60008c67ffffffffffffffff811115610c8657610c866153b8565b604051908082528060200260200182016040528015610caf578160200160208202803683370190505b50905060005b8d811015610da1578e8e82818110610ccf57610ccf6156a7565b9050602002016020810190610ce491906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7491906157a7565b828281518110610d8657610d866156a7565b6020908102919091010152610d9a816157c0565b9050610cb5565b506000610de28d8d808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508f9250612c9b915050565b9050610df18f8f848d8d612eb5565b6000610e3e8e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d612c9b565b90506000610e4c8383612f9d565b90507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc81610e7a85856157f8565b8787604051610e8c949392919061580b565b60405180910390a196505050505050509998505050505050505050565b33600081815260346020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610f72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610f7f8286868403612469565b506001949350505050565b6040805160018082528183019092526060916000919060208083019080368337019050509050600181600081518110610fc557610fc56156a7565b6020908102919091010152919050565b60003361071f818585612725565b6110156040518060a0016040528060008152602001606081526020016000815260200160008152602001600081525090565b61103f7fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded23361299b565b60008061104f606085018561583c565b905067ffffffffffffffff811115611069576110696153b8565b604051908082528060200260200182016040528015611092578160200160208202803683370190505b50905060005b6110a5606086018661583c565b90508110156111bd576110bb606086018661583c565b828181106110cb576110cb6156a7565b90506020020160208101906110e091906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa15801561114c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117091906157a7565b828281518110611182576111826156a7565b60200260200101818152505060008282815181106111a2576111a26156a7565b602002602001015111156111b557600192505b600101611098565b506111cf8161055e604087018761583c565b6111e460a085013561025e604087018761583c565b6111ec614eb3565b6111fa85610100013561302b565b60408501526000611237611211606088018861583c565b61121e60208a018a61583c565b61122b60408c018c61583c565b60009695505050505050565b905064e8d4a5100085604001518261124f91906158a4565b611259919061591f565b6112639082615987565b856040018181516112749190615987565b90525050604084015161128b906101208701613050565b506112e461129c608087018761583c565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506112df92505050610100880160e089016150d3565b612c9b565b81526000808060a088013586156114355760006113086101008b0160e08c016150d3565b73ffffffffffffffffffffffffffffffffffffffff1663a2d2657f61133060608d018d61583c565b8a8e8060800190611341919061583c565b6040518663ffffffff1660e01b81526004016113619594939291906159a7565b602060405180830381865afa15801561137e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a291906157a7565b905064e8d4a510006113b360355490565b10156113cc576113c5816103e8615a5f565b935061141f565b8551156113ed5785516035546113e3908390615a5f565b6113c59190615a76565b6040517f907d9a5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8184101561142f57839450611433565b8194505b505b600061144460608b018b61583c565b905067ffffffffffffffff81111561145e5761145e6153b8565b604051908082528060200260200182016040528015611487578160200160208202803683370190505b5090506000828511156118d25785156115485760005b6114aa60608d018d61583c565b90508110156115425785878a83815181106114c7576114c76156a7565b602002602001015102816114dd576114dd6158f0565b048382815181106114f0576114f06156a7565b60200260200101818152505082818151811061150e5761150e6156a7565b6020026020010151898281518110611528576115286156a7565b60209081029190910101805191909103905260010161149d565b50600190505b6000885167ffffffffffffffff811115611564576115646153b8565b60405190808252806020026020018201604052801561158d578160200160208202803683370190505b509050600061159c8d8061583c565b90501115611772576116056115b460608e018e61583c565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508a8e8060000190610853919061583c565b60005b61161560608e018e61583c565b905081101561176c57898181518110611630576116306156a7565b602002602001015182828151811061164a5761164a6156a7565b602002602001018181525050838181518110611668576116686156a7565b60200260200101518d8060600190611680919061583c565b83818110611690576116906156a7565b90506020020160208101906116a591906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611711573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173591906157a7565b61173f91906157f8565b8a8281518110611751576117516156a7565b6020908102919091010152611765816157c0565b9050611608565b506117d4565b60005b61178260608e018e61583c565b90508110156117d25789818151811061179d5761179d6156a7565b60200260200101518282815181106117b7576117b76156a7565b60209081029190910101526117cb816157c0565b9050611775565b505b6117fa6117e460608e018e61583c565b8b8f80604001906117f5919061583c565b612eb5565b61185d61180a60808e018e61583c565b80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508d60e00160208101906112df91906150d3565b60208901819052885161186f91612f9d565b885160208a01519196507fef3370e7b2b7c0f8f907344d48c31e679e6ff7ff055cad7846daf5ca2d5b30dc9187916118a6916157f8565b838c6040516118b8949392919061580b565b60405180910390a16118ca8786615705565b945050611b29565b84831115611aba5785156118e95785830392508593505b61190f6118f960608d018d61583c565b858e806040019061190a919061583c565b6131ca565b61191930846133c7565b50600161196f61192c60808d018d61583c565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506112df925050506101008e0160e08f016150d3565b602088015260005b61198460608d018d61583c565b9050811015611a745761199a60608d018d61583c565b828181106119aa576119aa6156a7565b90506020020160208101906119bf91906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015611a2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4f91906157a7565b838281518110611a6157611a616156a7565b6020908102919091010152600101611977565b5060208701516040517f8b1b306a5c3a19617717b1f37f2b4bea82978af8db2e859975f665fc40bc74b391611aad918691908690615a8a565b60405180910390a1611b29565b8515611b215782935060005b611ad360608d018d61583c565b9050811015611b1b57888181518110611aee57611aee6156a7565b6020026020010151838281518110611b0857611b086156a7565b6020908102919091010152600101611ac6565b50600190505b865160208801525b8015611bd15760005b611b3f60608d018d61583c565b9050811015611bcf57611bc7611b5b60e08e0160c08f016150d3565b848381518110611b6d57611b6d6156a7565b60200260200101518e8060600190611b85919061583c565b85818110611b9557611b956156a7565b9050602002016020810190611baa91906150d3565b73ffffffffffffffffffffffffffffffffffffffff16919061358d565b600101611b32565b505b838a526020808b0183905287015160608b015260355460808b015250979998505050505050505050565b60408051600180825281830190925260609160208083019080368337019050506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091507f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff1690638b32fa239082906370a0823190602401602060405180830381865afa158015611cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd491906157a7565b6040518263ffffffff1660e01b8152600401611cf291815260200190565b602060405180830381865afa158015611d0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3391906157a7565b81600081518110611d4657611d466156a7565b60200260200101818152505090565b32611df3576040805160018082528183019092526000916020808301908036833701905050905083600081518110611d8f57611d8f6156a7565b602002602001015181600081518110611daa57611daa6156a7565b6020026020010181815250507f2da97ade1b5402d92aa4ecf88302c7c6a98498999fe040468284622731550b3681604051611de59190615210565b60405180910390a150505050565b600282826000818110611e0857611e086156a7565b905060200201351115611e47576040517fdfc8737800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e8482826001818110611e5d57611e5d6156a7565b9050602002013584600081518110611e7757611e776156a7565b602002602001015161261c565b611eba576040517fdfc8737800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b60007f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633611eed828261299b565b611ef78585612c9b565b92505b505092915050565b60606096805461068e90615654565b7f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5633611f3d828261299b565b610853843085612725565b600054610100900460ff1615808015611f685750600054600160ff909116105b80611f825750303b158015611f82575060005460ff166001145b61200e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610f69565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561206c57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b612077826000613661565b60007f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663dd0fe31a6120bd610a52565b6040518263ffffffff1660e01b81526004016120db91815260200190565b600060405180830381865afa1580156120f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261213e9190810190615718565b9050805160011415806121b757507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1681600081518110612196576121966156a7565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614155b156121f9576121c4610a52565b6040517f4b1f57ad000000000000000000000000000000000000000000000000000000008152600401610f6991815260200190565b6122016138e7565b60985550801561226857600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f72f6db45f6a510a9f71a52fed1cf587666e7205545d52ee38b978222d6abaa5660048201523360248201526060907f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1373ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa15801561231f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123439190615aa9565b15801561242157506040517f91d148540000000000000000000000000000000000000000000000000000000081527fd00dd30c0065f8d96fd9ccd8989cf005814e3a3d416d39c86ff1cd1f1ba7ded260048201523360248201527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1373ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa1580156123fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241f9190615aa9565b155b1561245a576040517f7ab612a3000000000000000000000000000000000000000000000000000000008152336004820152602401610f69565b610a4787308888888888612c2e565b73ffffffffffffffffffffffffffffffffffffffff831661250b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff82166125ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600061263a60017001000000000000000000000000000000006157f8565b831682108061264c5750608083901c82115b159392505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152603460209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146108535781811015612718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610f69565b6108538484848403612469565b73ffffffffffffffffffffffffffffffffffffffff83166127c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff821661286b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff831660009081526033602052604090205481811015612921576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526033602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061298e9086815260200190565b60405180910390a3610853565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1316906391d1485490604401602060405180830381865afa158015612a2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a529190615aa9565b612268576040517f75000dc00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82166024820152604401610f69565b82826000818110612aba57612aba6156a7565b90506020020135600314612afa576040517ffc5fbdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090612beb9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639316906370a0823190602401602060405180830381865afa158015612b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612baf91906157a7565b858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250600192506139a0915050565b905061085373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216838361358d565b60606000612c3f86868b8787613dd9565b9050612c4b888a6133c7565b60005b85811015612c8e57612c8688838381518110612c6c57612c6c6156a7565b6020026020010151898985818110611b9557611b956156a7565b600101612c4e565b5098975050505050505050565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63931690638b32fa239082906370a0823190602401602060405180830381865afa158015612d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5691906157a7565b6040518263ffffffff1660e01b8152600401612d7491815260200190565b602060405180830381865afa158015612d91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612db591906157a7565b90508273ffffffffffffffffffffffffffffffffffffffff16639115900c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28387600081518110612e0857612e086156a7565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff909316600484015260248301919091526044820152606401602060405180830381865afa158015612e89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ead91906157a7565b949350505050565b600082826000818110612eca57612eca6156a7565b90506020020135600003612ee057506003612f3b565b82826000818110612ef357612ef36156a7565b90506020020135600203612f0957506002612f3b565b6040517f0947e0a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9484600081518110612f5157612f516156a7565b602002602001015184848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061403a915050565b50505050505050565b600080612fa960355490565b905064e8d4a51000811015612ff157612fc46103e884615a5f565b915064e8d4a5100081900380831015612fda5750815b8083039250612feb61dead82614573565b5061301a565b83156113ed57838161300382866157f8565b61300d9190615a5f565b6130179190615a76565b91505b6130243083614573565b5092915050565b6000806130366138e7565b905061304460985482614668565b60989190915592915050565b6000808313156107255782600061306e64e8d4a51000612710615a5f565b61307e6080860160608701615acb565b6bffffffffffffffffffffffff1661309c6040870160208801615acb565b6130b6906bffffffffffffffffffffffff166127106157f8565b6130c091906157f8565b6130ca9084615a5f565b6130d49190615705565b90506000826130e260355490565b6130ec9190615a5f565b90506000826131016040880160208901615acb565b613119906bffffffffffffffffffffffff1684615a5f565b6131239190615a76565b905061313b61313560208801886150d3565b82614573565b60008361314e6080890160608a01615acb565b613166906bffffffffffffffffffffffff1685615a5f565b6131709190615a76565b90506131856131356060890160408a016150d3565b604051828201808252965030907fd02a13a1b75d55c8b9ced16f3558e9004a0827559f52715b34bcc685d904c4599060200160405180910390a2505050505092915050565b6000828260008181106131df576131df6156a7565b905060200201356001036131f5575060036132ad565b82826000818110613208576132086156a7565b9050602002013560020361321e575060026132ad565b82826000818110613231576132316156a7565b90506020020135600303613247575060016132ad565b8282600081811061325a5761325a6156a7565b90506020020135600014801561326e575032155b1561327b575060036132ad565b6040517ffc5fbdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006132b860355490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015286907f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015613344573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061336891906157a7565b6133729190615a5f565b61337c9190615a76565b90506133bd818585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508792506139a0915050565b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661346a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff821660009081526033602052604090205481811015613520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610f69565b73ffffffffffffffffffffffffffffffffffffffff831660008181526033602090815260408083208686039055603580548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611eba9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526146d4565b600054610100900460ff166136f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b8151600003613733576040517fc52a9bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000001613799578061378f576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60978190556137d1565b80156137d1576040517f36307dff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a73ffffffffffffffffffffffffffffffffffffffff1663e065ce6e613815610a52565b6040518263ffffffff1660e01b815260040161383391815260200190565b60006040518083038186803b15801561384b57600080fd5b505afa15801561385f573d6000803e3d6000fd5b5050505081609690816138729190615b3f565b506122686040518060400160405280601481526020017f537472617465677920536861726520546f6b656e0000000000000000000000008152506040518060400160405280600381526020017f53535400000000000000000000000000000000000000000000000000000000008152506147e0565b6040517f8b32fa23000000000000000000000000000000000000000000000000000000008152670de0b6b3a764000060048201526000907f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff1690638b32fa2390602401602060405180830381865afa15801561397c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0791906157a7565b600032613c35576040517f2f18e2af00000000000000000000000000000000000000000000000000000000815260048101859052600a6024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c1690632f18e2af906044016060604051808303816000875af1158015613a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a649190615c59565b90925090508160006020020151858581518110613a8357613a836156a7565b6020908102919091010152816001602002015185613aa2866001615705565b81518110613ab257613ab26156a7565b60209081029190910101528085613aca866002615705565b81518110613ada57613ada6156a7565b60209081029190910101528085613af2866003615705565b81518110613b0257613b026156a7565b602090810291909101015260408051600480825260a0820190925260009181602001602082028036833750508451825192935091839150600090613b4857613b486156a7565b6020908102919091010152826001602002015181600181518110613b6e57613b6e6156a7565b6020026020010181815250508181600281518110613b8e57613b8e6156a7565b6020026020010181815250508181600381518110613bae57613bae6156a7565b6020026020010181815250507f872697b0ffc2830e88f47a2ddac8d40ec8898cb72bc036d3aaf8ce7960286a9060008083604051602001613bef9190615210565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052613c29939291615cde565b60405180910390a15050505b613c807f000000000000000000000000ae78736cd615f374d3085123a210448e74fc63937f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c86614881565b7f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c73ffffffffffffffffffffffffffffffffffffffff1663a824ae8b848481518110613cce57613cce6156a7565b602002602001015185856001613ce49190615705565b81518110613cf457613cf46156a7565b602002602001015186866002613d0a9190615705565b81518110613d1a57613d1a6156a7565b602002602001015187876003613d309190615705565b81518110613d4057613d406156a7565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815260048101949094526024840192909252604483015260648201526084810187905260a401600060405180830381600087803b158015613db557600080fd5b505af1158015613dc9573d6000803e3d6000fd5b50505050479050610878816148ac565b60608467ffffffffffffffff811115613df457613df46153b8565b604051908082528060200260200182016040528015613e1d578160200160208202803683370190505b50905060005b85811015613f0f57868682818110613e3d57613e3d6156a7565b9050602002016020810190613e5291906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee291906157a7565b828281518110613ef457613ef46156a7565b6020908102919091010152613f08816157c0565b9050613e23565b50613f1d86868686866131ca565b60005b8581101561403057818181518110613f3a57613f3a6156a7565b6020026020010151878783818110613f5457613f546156a7565b9050602002016020810190613f6991906150d3565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff991906157a7565b61400391906157f8565b828281518110614015576140156156a7565b6020908102919091010152614029816157c0565b9050613f20565b5095945050505050565b6000326142d0576040517f27e0416300000000000000000000000000000000000000000000000000000000815260048101859052600a6024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c16906327e04163906044016060604051808303816000875af11580156140da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140fe9190615c59565b9092509050816000602002015185858151811061411d5761411d6156a7565b602090810291909101015281600160200201518561413c866001615705565b8151811061414c5761414c6156a7565b60209081029190910101528085614164866002615705565b81518110614174576141746156a7565b6020908102919091010152808561418c866003615705565b8151811061419c5761419c6156a7565b602090810291909101015260408051600480825260a08201909252600091816020016020820280368337505084518251929350918391506000906141e2576141e26156a7565b6020908102919091010152826001602002015181600181518110614208576142086156a7565b6020026020010181815250508181600281518110614228576142286156a7565b6020026020010181815250508181600381518110614248576142486156a7565b6020026020010181815250507f872697b0ffc2830e88f47a2ddac8d40ec8898cb72bc036d3aaf8ce7960286a90600160008360405160200161428a9190615210565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526142c4939291615cde565b60405180910390a15050505b6142d984614930565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015614363573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438791906157a7565b90507f00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c73ffffffffffffffffffffffffffffffffffffffff166355362f4d858585815181106143d8576143d86156a7565b6020026020010151868660016143ee9190615705565b815181106143fe576143fe6156a7565b6020026020010151878760026144149190615705565b81518110614424576144246156a7565b60200260200101518888600361443a9190615705565b8151811061444a5761444a6156a7565b60200260200101516040518663ffffffff1660e01b8152600401614487949392919093845260208401929092526040830152606082015260800190565b6000604051808303818588803b1580156144a057600080fd5b505af11580156144b4573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528493507f000000000000000000000000ae78736cd615f374d3085123a210448e74fc639373ffffffffffffffffffffffffffffffffffffffff1692506370a082319150602401602060405180830381865afa158015614545573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061456991906157a7565b612ead91906157f8565b73ffffffffffffffffffffffffffffffffffffffff82166145f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610f69565b80603560008282546146029190615705565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152603360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60008282111561469e578264e8d4a5100061468382856157f8565b61468d9190615a5f565b6146979190615a76565b9050610725565b81831115610725578264e8d4a510006146b784836157f8565b6146c19190615a5f565b6146cb9190615a76565b61087890615cff565b6000614736826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166149cc9092919063ffffffff16565b805190915015611eba57808060200190518101906147549190615aa9565b611eba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610f69565b600054610100900460ff16614877576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b61226882826149db565b61488b8383614a8b565b611eba73ffffffffffffffffffffffffffffffffffffffff84168383614b49565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561491457600080fd5b505af1158015614928573d6000803e3d6000fd5b505050505050565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b1580156149b857600080fd5b505af1158015610939573d6000803e3d6000fd5b6060612ead8484600085614ccb565b600054610100900460ff16614a72576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610f69565b6036614a7e8382615b3f565b506037611eba8282615b3f565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff82811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015614b01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b2591906157a7565b11156122685761226873ffffffffffffffffffffffffffffffffffffffff83168260005b801580614be957506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614bc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be791906157a7565b155b614c75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610f69565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611eba9084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016135df565b606082471015614d5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610f69565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614d869190615d37565b60006040518083038185875af1925050503d8060008114614dc3576040519150601f19603f3d011682016040523d82523d6000602084013e614dc8565b606091505b5091509150610a478783838760608315614e6a578251600003614e635773ffffffffffffffffffffffffffffffffffffffff85163b614e63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610f69565b5081612ead565b612ead8383815115614e7f5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f699190614f3f565b60405180604001604052806002906020820280368337509192915050565b60005b83811015614eec578181015183820152602001614ed4565b50506000910152565b60008151808452614f0d816020860160208601614ed1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006108786020830184614ef5565b73ffffffffffffffffffffffffffffffffffffffff81168114614f7457600080fd5b50565b60008060408385031215614f8a57600080fd5b8235614f9581614f52565b946020939093013593505050565b60008083601f840112614fb557600080fd5b50813567ffffffffffffffff811115614fcd57600080fd5b6020830191508360208260051b8501011115614fe857600080fd5b9250929050565b60008060006040848603121561500457600080fd5b83359250602084013567ffffffffffffffff81111561502257600080fd5b61502e86828701614fa3565b9497909650939450505050565b60008060006060848603121561505057600080fd5b833561505b81614f52565b9250602084013561506b81614f52565b929592945050506040919091013590565b60008060006040848603121561509157600080fd5b833567ffffffffffffffff8111156150a857600080fd5b6150b486828701614fa3565b90945092505060208401356150c881614f52565b809150509250925092565b6000602082840312156150e557600080fd5b813561087881614f52565b600081518084526020808501945080840160005b8381101561513657815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101615104565b509495945050505050565b60208152600061087860208301846150f0565b6000806000806000806080878903121561516d57600080fd5b86359550602087013561517f81614f52565b9450604087013567ffffffffffffffff8082111561519c57600080fd5b6151a88a838b01614fa3565b909650945060608901359150808211156151c157600080fd5b506151ce89828a01614fa3565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015615136578151875295820195908201906001016151f4565b60208152600061087860208301846151e0565b60408152600061523660408301856150f0565b828103602084015261524881856151e0565b95945050505050565b600080600080600080600080600060a08a8c03121561526f57600080fd5b893567ffffffffffffffff8082111561528757600080fd5b6152938d838e01614fa3565b909b50995060208c01359150808211156152ac57600080fd5b6152b88d838e01614fa3565b909950975060408c013591506152cd82614f52565b90955060608b013590808211156152e357600080fd5b6152ef8d838e01614fa3565b909650945060808c013591508082111561530857600080fd5b506153158c828d01614fa3565b915080935050809150509295985092959850929598565b60006020828403121561533e57600080fd5b813567ffffffffffffffff81111561535557600080fd5b82016101a0818503121561087857600080fd5b60208152815160208201526000602083015160a0604084015261538e60c08401826151e0565b90506040840151606084015260608401516080840152608084015160a08401528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561542e5761542e6153b8565b604052919050565b600067ffffffffffffffff821115615450576154506153b8565b5060051b60200190565b600082601f83011261546b57600080fd5b8135602061548061547b83615436565b6153e7565b82815260059290921b8401810191818101908684111561549f57600080fd5b8286015b848110156154ba57803583529183019183016154a3565b509695505050505050565b6000806000604084860312156154da57600080fd5b833567ffffffffffffffff808211156154f257600080fd5b6154fe8783880161545a565b9450602086013591508082111561551457600080fd5b5061502e86828701614fa3565b6000806040838503121561553457600080fd5b823567ffffffffffffffff81111561554b57600080fd5b6155578582860161545a565b925050602083013561556881614f52565b809150509250929050565b6000806040838503121561558657600080fd5b823561559181614f52565b9150602083013561556881614f52565b600060208083850312156155b457600080fd5b823567ffffffffffffffff808211156155cc57600080fd5b818501915085601f8301126155e057600080fd5b8135818111156155f2576155f26153b8565b615622847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016153e7565b9150808252868482850101111561563857600080fd5b8084840185840137600090820190930192909252509392505050565b600181811c9082168061566857607f821691505b6020821081036156a1577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610725576107256156d6565b6000602080838503121561572b57600080fd5b825167ffffffffffffffff81111561574257600080fd5b8301601f8101851361575357600080fd5b805161576161547b82615436565b81815260059190911b8201830190838101908783111561578057600080fd5b928401925b82841015610a4757835161579881614f52565b82529284019290840190615785565b6000602082840312156157b957600080fd5b5051919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036157f1576157f16156d6565b5060010190565b81810381811115610725576107256156d6565b84815283602082015260806040820152600061582a60808301856151e0565b8281036060840152610a4781856151e0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261587157600080fd5b83018035915067ffffffffffffffff82111561588c57600080fd5b6020019150600581901b3603821315614fe857600080fd5b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156158dc576158dc6156d6565b8181058314821517610725576107256156d6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261592e5761592e6158f0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615615982576159826156d6565b500590565b8082018281126000831280158216821582161715611efa57611efa6156d6565b6060808252810185905260008660808301825b888110156159f75782356159cd81614f52565b73ffffffffffffffffffffffffffffffffffffffff168252602092830192909101906001016159ba565b508381036020850152615a0a81886151e0565b91505082810360408401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115615a4557600080fd5b8360051b8086602084013701602001979650505050505050565b8082028115828204841417610725576107256156d6565b600082615a8557615a856158f0565b500490565b83815282602082015260606040820152600061524860608301846151e0565b600060208284031215615abb57600080fd5b8151801515811461087857600080fd5b600060208284031215615add57600080fd5b81356bffffffffffffffffffffffff8116811461087857600080fd5b601f821115611eba57600081815260208120601f850160051c81016020861015615b205750805b601f850160051c820191505b8181101561492857828155600101615b2c565b815167ffffffffffffffff811115615b5957615b596153b8565b615b6d81615b678454615654565b84615af9565b602080601f831160018114615bc05760008415615b8a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614928565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015615c0d57888601518255948401946001909101908401615bee565b5085821015615c4957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60008060608385031215615c6c57600080fd5b83601f840112615c7b57600080fd5b6040516040810181811067ffffffffffffffff82111715615c9e57615c9e6153b8565b8060405250806040850186811115615cb557600080fd5b855b81811015615ccf578051835260209283019201615cb7565b50519196919550909350505050565b83151581528260208201526060604082015260006152486060830184614ef5565b60007f80000000000000000000000000000000000000000000000000000000000000008203615d3057615d306156d6565b5060000390565b60008251615d49818460208701614ed1565b919091019291505056fea26469706673582212205740574dd20ba806085e79746ceccc677ad2b2edf42947d6d5255ed62ba8944c64736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd13000000000000000000000000000000000000000000000000000000000000000100000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
-----Decoded View---------------
Arg [0] : assetGroupRegistry_ (address): 0x1Aa2a802BA25669531Ffd2b1fF8ae94f3D87f41A
Arg [1] : accessControl_ (address): 0x7b533e72E0cDC63AacD8cDB926AC402b846Fbd13
Arg [2] : assetGroupId_ (uint256): 1
Arg [3] : rocketSwapRouter_ (address): 0x16D5A408e807db8eF7c578279BEeEe6b228f1c1C
Arg [4] : weth_ (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a
Arg [1] : 0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd13
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [3] : 00000000000000000000000016d5a408e807db8ef7c578279beeee6b228f1c1c
Arg [4] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.