Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
SmartVaultManager
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 1111 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/utils/math/Math.sol"; import "../interfaces/IAction.sol"; import "../interfaces/IAssetGroupRegistry.sol"; import "../interfaces/IDepositManager.sol"; import "../interfaces/IGuardManager.sol"; import "../interfaces/IMasterWallet.sol"; import "../interfaces/IRiskManager.sol"; import "../interfaces/ISmartVault.sol"; import "../interfaces/ISmartVaultManager.sol"; import "../interfaces/IStrategy.sol"; import "../interfaces/IStrategyRegistry.sol"; import "../interfaces/IUsdPriceFeedManager.sol"; import "../interfaces/IWithdrawalManager.sol"; import "../interfaces/CommonErrors.sol"; import "../interfaces/Constants.sol"; import "../interfaces/RequestType.sol"; import "../access/SpoolAccessControllable.sol"; import "../libraries/ArrayMapping.sol"; import "../libraries/uint16a16Lib.sol"; import "../libraries/ReallocationLib.sol"; struct VaultSyncUserBag { address[] tokens; address[] strategies; bytes[] metadata; uint256[] nftBalances; } /** * @dev Requires roles: * - ROLE_MASTER_WALLET_MANAGER * - ROLE_SMART_VAULT_MANAGER */ contract SmartVaultManager is ISmartVaultManager, SpoolAccessControllable { using uint16a16Lib for uint16a16; using SafeERC20 for IERC20; using ArrayMappingUint256 for mapping(uint256 => uint256); using ArrayMappingAddress for mapping(uint256 => address); IDepositManager private immutable _depositManager; IWithdrawalManager private immutable _withdrawalManager; /// @notice Strategy registry IStrategyRegistry private immutable _strategyRegistry; /// @notice Asset Group registry IAssetGroupRegistry private immutable _assetGroupRegistry; /// @notice Risk manager. IRiskManager private immutable _riskManager; /// @notice Master wallet IMasterWallet private immutable _masterWallet; /// @notice Price feed manager IUsdPriceFeedManager private immutable _priceFeedManager; address private immutable _ghostStrategy; /* ========== STATE VARIABLES ========== */ /// @notice Smart Vault registry mapping(address => bool) internal _smartVaultRegistry; /// @notice Smart Vault - asset group ID registry mapping(address => uint256) internal _smartVaultAssetGroups; /// @notice Smart Vault strategy registry mapping(address => address[]) internal _smartVaultStrategies; /// @notice Smart vault fees mapping(address => SmartVaultFees) internal _smartVaultFees; /// @notice Smart Vault strategy allocations mapping(address => uint16a16) internal _smartVaultAllocations; /// @notice Current flush index and index to sync for given Smart Vault mapping(address => FlushIndex) internal _flushIndexes; /** * @notice DHW indexes for given Smart Vault and flush index * @dev smart vault => flush index => DHW indexes */ mapping(address => mapping(uint256 => uint16a16)) internal _dhwIndexes; /** * @notice Timestamp of the last DHW that was synced * @dev smart vault => dhw timestamp */ mapping(address => uint256) _lastDhwTimestampSynced; constructor( ISpoolAccessControl accessControl_, IAssetGroupRegistry assetGroupRegistry_, IRiskManager riskManager_, IDepositManager depositManager_, IWithdrawalManager withdrawalManager_, IStrategyRegistry strategyRegistry_, IMasterWallet masterWallet_, IUsdPriceFeedManager priceFeedManager_, address ghostStrategy ) SpoolAccessControllable(accessControl_) { if (address(assetGroupRegistry_) == address(0)) revert ConfigurationAddressZero(); if (address(riskManager_) == address(0)) revert ConfigurationAddressZero(); if (address(depositManager_) == address(0)) revert ConfigurationAddressZero(); if (address(withdrawalManager_) == address(0)) revert ConfigurationAddressZero(); if (address(strategyRegistry_) == address(0)) revert ConfigurationAddressZero(); if (address(masterWallet_) == address(0)) revert ConfigurationAddressZero(); if (address(priceFeedManager_) == address(0)) revert ConfigurationAddressZero(); if (ghostStrategy == address(0)) revert ConfigurationAddressZero(); _assetGroupRegistry = assetGroupRegistry_; _riskManager = riskManager_; _depositManager = depositManager_; _withdrawalManager = withdrawalManager_; _strategyRegistry = strategyRegistry_; _masterWallet = masterWallet_; _priceFeedManager = priceFeedManager_; _ghostStrategy = ghostStrategy; } /* ========== VIEW FUNCTIONS ========== */ /** * @notice SmartVault strategies */ function strategies(address smartVault) external view returns (address[] memory) { return _smartVaultStrategies[smartVault]; } /** * @notice SmartVault strategy allocations */ function allocations(address smartVault) external view returns (uint16a16) { return _smartVaultAllocations[smartVault]; } /** * @notice SmartVault asset group ID */ function assetGroupId(address smartVault) external view returns (uint256) { return _smartVaultAssetGroups[smartVault]; } function depositRatio(address smartVault) external view returns (uint256[] memory) { return _depositManager.getDepositRatio( _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]), _smartVaultAllocations[smartVault], _smartVaultStrategies[smartVault] ); } /** * @notice SmartVault latest flush index */ function getLatestFlushIndex(address smartVault) external view returns (uint256) { return _flushIndexes[smartVault].current; } /** * @notice DHW indexes that were active at given flush index */ function dhwIndexes(address smartVault, uint256 flushIndex) external view returns (uint16a16) { return _dhwIndexes[smartVault][flushIndex]; } /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /* ========== DEPOSIT/WITHDRAW ========== */ function redeem(RedeemBag calldata bag, address receiver, bool doFlush) external whenNotPaused checkNonReentrant returns (uint256) { return _redeem(bag, receiver, msg.sender, msg.sender, doFlush); } function redeemFor(RedeemBag calldata bag, address owner, bool doFlush) external whenNotPaused checkNonReentrant returns (uint256) { _checkRole(ROLE_SMART_VAULT_ALLOW_REDEEM, bag.smartVault); _checkSmartVaultRole(bag.smartVault, ROLE_SMART_VAULT_ADMIN, msg.sender); return _redeem(bag, owner, owner, msg.sender, doFlush); } function redeemFast(RedeemBag calldata bag, uint256[][] calldata withdrawalSlippages) external whenNotPaused nonReentrant returns (uint256[] memory) { return _redeemFast(bag, withdrawalSlippages, msg.sender); } function redeemFastView(RedeemBag calldata bag, uint256[][] calldata withdrawalSlippages, address redeemer) external whenNotPaused returns (uint256[] memory) { if (!_isViewExecution()) { revert OnlyViewExecution(tx.origin); } return _redeemFast(bag, withdrawalSlippages, redeemer); } function _redeemFast(RedeemBag calldata bag, uint256[][] calldata withdrawalSlippages, address redeemer) private returns (uint256[] memory) { _onlyRegisteredSmartVault(bag.smartVault); address[] memory strategies_ = _smartVaultStrategies[bag.smartVault]; uint256 assetGroupId_ = _smartVaultAssetGroups[bag.smartVault]; address[] memory tokens = _assetGroupRegistry.listAssetGroup(assetGroupId_); if (strategies_.length != withdrawalSlippages.length) { revert InvalidArrayLength(); } _syncSmartVault(bag.smartVault, strategies_, tokens, false); uint256 flushIndexToSync = _flushIndexes[bag.smartVault].toSync; _depositManager.claimSmartVaultTokens( bag.smartVault, bag.nftIds, bag.nftAmounts, tokens, redeemer, redeemer, flushIndexToSync ); return _withdrawalManager.redeemFast( bag, RedeemFastExtras(strategies_, tokens, assetGroupId_, redeemer, withdrawalSlippages) ); } function deposit(DepositBag calldata bag) external whenNotPaused checkNonReentrant returns (uint256) { _onlyRegisteredSmartVault(bag.smartVault); return _depositAssets(bag); } function claimSmartVaultTokens(address smartVault, uint256[] calldata nftIds, uint256[] calldata nftAmounts) public whenNotPaused checkNonReentrant returns (uint256) { _onlyRegisteredSmartVault(smartVault); address[] memory tokens = _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]); _syncSmartVault(smartVault, _smartVaultStrategies[smartVault], tokens, false); uint256 flushIndexToSync = _flushIndexes[smartVault].toSync; return _depositManager.claimSmartVaultTokens( smartVault, nftIds, nftAmounts, tokens, msg.sender, msg.sender, flushIndexToSync ); } function claimWithdrawal( address smartVault, uint256[] calldata nftIds, uint256[] calldata nftAmounts, address receiver ) public whenNotPaused checkNonReentrant returns (uint256[] memory, uint256) { _onlyRegisteredSmartVault(smartVault); uint256 assetGroupId_ = _smartVaultAssetGroups[smartVault]; address[] memory tokens = _assetGroupRegistry.listAssetGroup(assetGroupId_); _syncSmartVault(smartVault, _smartVaultStrategies[smartVault], tokens, false); return _withdrawalManager.claimWithdrawal( WithdrawalClaimBag( smartVault, nftIds, nftAmounts, receiver, msg.sender, assetGroupId_, tokens, _flushIndexes[smartVault].toSync ) ); } function recoverPendingDeposits(address smartVault) external whenNotPaused checkNonReentrant { _checkRole(ROLE_SPOOL_ADMIN, msg.sender); _onlyRegisteredSmartVault(smartVault); _depositManager.recoverPendingDeposits( smartVault, _flushIndexes[smartVault].current, _smartVaultStrategies[smartVault], _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]), _strategyRegistry.emergencyWithdrawalWallet() ); } /* ========== REGISTRY ========== */ function registerSmartVault(address smartVault, SmartVaultRegistrationForm calldata registrationForm) external whenNotPaused { _checkRole(ROLE_SMART_VAULT_INTEGRATOR, msg.sender); if (_smartVaultRegistry[smartVault]) { revert SmartVaultAlreadyRegistered(); } // set asset group _smartVaultAssetGroups[smartVault] = registrationForm.assetGroupId; // set strategies _smartVaultStrategies[smartVault] = registrationForm.strategies; // set smart vault fees _smartVaultFees[smartVault] = SmartVaultFees( registrationForm.managementFeePct, registrationForm.depositFeePct, registrationForm.performanceFeePct ); // set allocation _smartVaultAllocations[smartVault] = registrationForm.strategyAllocation; // update registry _smartVaultRegistry[smartVault] = true; emit SmartVaultRegistered(smartVault, registrationForm); } function removeStrategyFromVaults(address strategy, address[] calldata vaults, bool disableStrategy) external checkNonReentrant { _checkRole(ROLE_SPOOL_ADMIN, msg.sender); unchecked { for (uint256 i; i < vaults.length; ++i) { address smartVault = vaults[i]; address[] memory strategies_ = _smartVaultStrategies[vaults[i]]; for (uint256 j; j < strategies_.length; ++j) { if (strategies_[j] == strategy) { _smartVaultStrategies[smartVault][j] = _ghostStrategy; _smartVaultAllocations[smartVault] = _smartVaultAllocations[smartVault].set(j, 0); emit StrategyRemovedFromVault(strategy, vaults[i]); break; } } } } if (disableStrategy) { _strategyRegistry.removeStrategy(strategy); } } /* ========== BOOKKEEPING ========== */ function flushSmartVault(address smartVault) public whenNotPaused checkNonReentrant { _onlyRegisteredSmartVault(smartVault); address[] memory strategies_ = _smartVaultStrategies[smartVault]; // do not allow flushing if vault consists only of ghost strategies _nonGhostVault(strategies_); _flushSmartVault( smartVault, _smartVaultAllocations[smartVault], strategies_, _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]) ); } function syncSmartVault(address smartVault, bool revertIfError) public whenNotPaused checkNonReentrant { _onlyRegisteredSmartVault(smartVault); _syncSmartVault( smartVault, _smartVaultStrategies[smartVault], _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]), revertIfError ); } function reallocate(ReallocateParamBag calldata reallocateParams) external whenNotPaused nonReentrant { // Can only be called by a reallocator. if (!_isViewExecution()) { _checkRole(ROLE_REALLOCATOR, msg.sender); } if (reallocateParams.validUntil < block.timestamp) { // Check if reallocation parameters are still valid. revert ReallocationParametersExpired(); } if (reallocateParams.smartVaults.length == 0) { // Check if there is anything to reallocate. return; } if ( reallocateParams.strategies.length != reallocateParams.swapInfo.length || reallocateParams.strategies.length != reallocateParams.depositSlippages.length || reallocateParams.strategies.length != reallocateParams.withdrawalSlippages.length ) { revert InvalidArrayLength(); } uint256 assetGroupId_ = _smartVaultAssetGroups[reallocateParams.smartVaults[0]]; address[] memory tokens = _assetGroupRegistry.listAssetGroup(assetGroupId_); for (uint256 i; i < reallocateParams.smartVaults.length; ++i) { address smartVault = reallocateParams.smartVaults[i]; // Check that all smart vaults are registered. _onlyRegisteredSmartVault(smartVault); // Check that all smart vaults use the same asset group. if (_smartVaultAssetGroups[smartVault] != assetGroupId_) { revert NotSameAssetGroup(); } // Check that any smart vault does not have statically set allocation. if (_riskManager.getRiskProvider(smartVault) == address(0)) { revert StaticAllocationSmartVault(); } // Sync smart vault. _syncSmartVault(smartVault, _smartVaultStrategies[smartVault], tokens, false); // Set new allocation. uint16a16 newAllocations = _riskManager.calculateAllocation(smartVault, _smartVaultStrategies[smartVault]); _smartVaultAllocations[smartVault] = newAllocations; emit SmartVaultReallocated(smartVault, newAllocations); } ReallocationParameterBag memory reallocationParameterBag = ReallocationParameterBag({ assetGroupRegistry: _assetGroupRegistry, priceFeedManager: _priceFeedManager, masterWallet: _masterWallet, assetGroupId: assetGroupId_, swapInfo: reallocateParams.swapInfo, depositSlippages: reallocateParams.depositSlippages, withdrawalSlippages: reallocateParams.withdrawalSlippages, exchangeRateSlippages: reallocateParams.exchangeRateSlippages }); // Do the reallocation. ReallocationLib.reallocate( reallocateParams.smartVaults, reallocateParams.strategies, _ghostStrategy, reallocationParameterBag, _smartVaultStrategies, _smartVaultAllocations ); } /* ========== PRIVATE/INTERNAL FUNCTIONS ========== */ /** * @dev Claim strategy shares, account for withdrawn assets and sync SVTs for all new DHW runs * Invariants: * - There can't be more than once un-synced flush index per vault at any given time. * - Flush index can't be synced, if all DHWs haven't been completed yet. */ function _syncSmartVault( address smartVault, address[] memory strategies_, address[] memory tokens, bool revertIfError ) private { FlushIndex memory flushIndex = _flushIndexes[smartVault]; if (flushIndex.current == flushIndex.toSync) { if (revertIfError) { revert NothingToSync(); } return; } // Pack values to avoid stack depth limit uint16a16 indexes = _dhwIndexes[smartVault][flushIndex.toSync]; uint16a16[2] memory packedIndexes = [indexes, _getPreviousDhwIndexes(smartVault, flushIndex.toSync)]; // If DHWs haven't been run yet, we can't sync if (!_areAllDhwRunsCompleted(_strategyRegistry.currentIndex(strategies_), indexes, strategies_, revertIfError)) { return; } SmartVaultFees memory fees = _smartVaultFees[smartVault]; // Pack values to avoid stack depth limit uint256[2] memory packedParams = [flushIndex.toSync, _lastDhwTimestampSynced[smartVault]]; // SYNC WITHDRAWALS _withdrawalManager.syncWithdrawals(smartVault, flushIndex.toSync, strategies_, indexes); // SYNC DEPOSITS DepositSyncResult memory syncResult = _depositManager.syncDeposits(smartVault, packedParams, strategies_, packedIndexes, tokens, fees); emit SmartVaultSynced(smartVault, flushIndex.toSync); flushIndex.toSync++; _flushIndexes[smartVault] = flushIndex; _lastDhwTimestampSynced[smartVault] = syncResult.dhwTimestamp; } /** * @dev Check whether all DHW runs were completed for given indexes */ function _areAllDhwRunsCompleted( uint256[] memory currentStrategyIndexes, uint16a16 dhwIndexes_, address[] memory strategies_, bool revertIfError ) private view returns (bool) { for (uint256 i; i < strategies_.length; ++i) { if (strategies_[i] == _ghostStrategy) { continue; } if (dhwIndexes_.get(i) >= currentStrategyIndexes[i]) { if (revertIfError) { revert DhwNotRunYetForIndex(strategies_[i], dhwIndexes_.get(i)); } return false; } } return true; } /** * @dev Calculate number of SVTs that haven't been synced yet after DHW runs * DHW has minted strategy shares, but vaults haven't claimed them yet. * Includes management fees (percentage of assets under management, distributed throughout a year) and deposit fees . * Invariants: * - There can't be more than once un-synced flush index per vault at any given time. * - Flush index can't be synced, if all DHWs haven't been completed yet. * * Can be used to retrieve the number of SSTs the vault would claim during sync. */ function simulateSync(address smartVault) public view returns (uint256 oldTotalSVTs, uint256, uint256, uint256[] memory) { address[] memory strategies_; SmartVaultFees memory fees; uint16a16 indexes; FlushIndex memory flushIndex = _flushIndexes[smartVault]; { strategies_ = _smartVaultStrategies[smartVault]; oldTotalSVTs = ISmartVault(smartVault).totalSupply(); fees = _smartVaultFees[smartVault]; indexes = _dhwIndexes[smartVault][flushIndex.toSync]; } if (flushIndex.current == flushIndex.toSync) { return (oldTotalSVTs, 0, 0, new uint256[](strategies_.length)); } // If DHWs haven't been run yet, we can't sync if (!_areAllDhwRunsCompleted(_strategyRegistry.currentIndex(strategies_), indexes, strategies_, false)) { return (oldTotalSVTs, 0, 0, new uint256[](strategies_.length)); } uint256[2] memory packedParams; uint16a16 previousIndexes; address[] memory tokens; { tokens = _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]); previousIndexes = _getPreviousDhwIndexes(smartVault, flushIndex.toSync); uint256 lastDhwTimestamp = _lastDhwTimestampSynced[smartVault]; packedParams = [flushIndex.toSync, lastDhwTimestamp]; } SimulateDepositParams memory params; { params = SimulateDepositParams(smartVault, packedParams, strategies_, tokens, indexes, previousIndexes, fees); } DepositSyncResult memory syncResult = _depositManager.syncDepositsSimulate(params); return (oldTotalSVTs, syncResult.mintedSVTs, syncResult.feeSVTs, syncResult.sstShares); } /** * @dev Calculate how many SVTs a user would receive, if he were to burn his NFTs. * For NFTs that are part of a vault flush that haven't been synced yet, simulate vault sync. * We have to simulate sync in correct order, to calculate management fees. * * Invariants: * - There can't be more than once un-synced flush index per vault at any given time. * - Flush index can't be synced, if all DHWs haven't been completed yet. * - W-NFTs and NFTs with fractional balance of 0 will be skipped. */ function simulateSyncWithBurn(address smartVault, address userAddress, uint256[] memory nftIds) public view returns (uint256 newBalance) { VaultSyncUserBag memory bag2; FlushIndex memory flushIndex; { bag2.metadata = ISmartVault(smartVault).getMetadata(nftIds); bag2.nftBalances = ISmartVault(smartVault).balanceOfFractionalBatch(userAddress, nftIds); bag2.tokens = _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[smartVault]); bag2.strategies = _smartVaultStrategies[smartVault]; flushIndex = _flushIndexes[smartVault]; } // Burn any NFTs that have already been synced newBalance += _simulateNFTBurn(smartVault, nftIds, bag2, 0, flushIndex, false); // Check if latest flush index has already been synced. if (flushIndex.toSync == flushIndex.current) { return newBalance; } uint16a16 indexes = _dhwIndexes[smartVault][flushIndex.toSync]; // If DHWs haven't been run yet, we can't sync if (!_areAllDhwRunsCompleted(_strategyRegistry.currentIndex(bag2.strategies), indexes, bag2.strategies, false)) { return newBalance; } uint256[2] memory packedParams; { uint256 lastDhwTimestamp = _lastDhwTimestampSynced[smartVault]; packedParams = [flushIndex.toSync, lastDhwTimestamp]; } SmartVaultFees memory fees = _smartVaultFees[smartVault]; SimulateDepositParams memory params = SimulateDepositParams( smartVault, packedParams, bag2.strategies, bag2.tokens, indexes, _getPreviousDhwIndexes(smartVault, flushIndex.toSync), fees ); // Simulate deposit sync (DHW) DepositSyncResult memory syncResult = _depositManager.syncDepositsSimulate(params); // Burn any NFTs that would be synced as part of this flush cycle newBalance += _simulateNFTBurn(smartVault, nftIds, bag2, syncResult.mintedSVTs, flushIndex, true); return newBalance; } /** * @dev Check how many SVTs would be received by burning the given array of NFTs * @param smartVault vault address * @param nftIds array of D-NFT ids * @param bag NFT balances, token addresses and NFT metadata * @param mintedSVTs amount of SVTs minted when syncing flush index * @param flushIndex global flush index * @param onlyCurrentFlushIndex whether to burn NFTs for current synced flush index or previously synced ones */ function _simulateNFTBurn( address smartVault, uint256[] memory nftIds, VaultSyncUserBag memory bag, uint256 mintedSVTs, FlushIndex memory flushIndex, bool onlyCurrentFlushIndex ) private view returns (uint256 SVTs) { for (uint256 i; i < nftIds.length; ++i) { // Skip W-NFTs if (nftIds[i] > MAXIMAL_DEPOSIT_ID) continue; // Skip D-NFTs with 0 balance if (bag.nftBalances[i] == 0) continue; DepositMetadata memory data = abi.decode(bag.metadata[i], (DepositMetadata)); // we're burning NFTs that have already been synced previously if (!onlyCurrentFlushIndex && data.flushIndex >= flushIndex.toSync) continue; // we're burning NFTs for current synced flushIndex if (onlyCurrentFlushIndex && data.flushIndex != flushIndex.toSync) continue; SVTs += _depositManager.getClaimedVaultTokensPreview( smartVault, data, bag.nftBalances[i], mintedSVTs, bag.tokens ); } } function _redeem(RedeemBag calldata bag, address receiver, address owner, address executor, bool doFlush) internal returns (uint256 nftId) { _onlyRegisteredSmartVault(bag.smartVault); address[] memory strategies_ = _smartVaultStrategies[bag.smartVault]; // do not allow redeemal if vault consists only of ghost strategies _nonGhostVault(strategies_); address[] memory tokens = _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[bag.smartVault]); _syncSmartVault(bag.smartVault, strategies_, tokens, false); FlushIndex memory flushIndex = _flushIndexes[bag.smartVault]; { uint256 flushIndexToSync = flushIndex.toSync; _depositManager.claimSmartVaultTokens( bag.smartVault, bag.nftIds, bag.nftAmounts, tokens, owner, executor, flushIndexToSync ); } nftId = _withdrawalManager.redeem( bag, RedeemExtras({receiver: receiver, owner: owner, executor: executor, flushIndex: flushIndex.current}) ); if (doFlush) { _flushSmartVault(bag.smartVault, _smartVaultAllocations[bag.smartVault], strategies_, tokens); } } /** * @dev Prepare deposits to be processed in the next DHW cycle. * - first sync vault * - update vault rewards * - optionally trigger flush right after */ function _depositAssets(DepositBag calldata bag) internal returns (uint256) { address[] memory strategies_ = _smartVaultStrategies[bag.smartVault]; // do not allow deposits if vault consists only of ghost strategies _nonGhostVault(strategies_); address[] memory tokens = _assetGroupRegistry.listAssetGroup(_smartVaultAssetGroups[bag.smartVault]); uint16a16 allocations_ = _smartVaultAllocations[bag.smartVault]; _syncSmartVault(bag.smartVault, strategies_, tokens, false); uint256 depositId = _depositManager.depositAssets( bag, DepositExtras({ depositor: msg.sender, tokens: tokens, allocations: allocations_, strategies: strategies_, flushIndex: _flushIndexes[bag.smartVault].current }) ); for (uint256 i; i < bag.assets.length; ++i) { IERC20(tokens[i]).safeTransferFrom(msg.sender, address(_masterWallet), bag.assets[i]); } if (bag.doFlush) { _flushSmartVault(bag.smartVault, allocations_, strategies_, tokens); } return depositId; } /** * @dev Mark accrued deposits and withdrawals ready for the next DHW cycle */ function _flushSmartVault( address smartVault, uint16a16 allocations_, address[] memory strategies_, address[] memory tokens ) private { FlushIndex memory flushIndex = _flushIndexes[smartVault]; // Flushing without having synced the previous flush is not allowed if (flushIndex.toSync != flushIndex.current) revert VaultNotSynced(); // need to flush withdrawal before flushing deposit uint16a16 flushDhwIndexes = _withdrawalManager.flushSmartVault(smartVault, flushIndex.current, strategies_); uint16a16 flushDhwIndexes2 = _depositManager.flushSmartVault(smartVault, flushIndex.current, strategies_, allocations_, tokens); if (uint16a16.unwrap(flushDhwIndexes2) > 0) { flushDhwIndexes = flushDhwIndexes2; } if (uint16a16.unwrap(flushDhwIndexes) == 0) revert NothingToFlush(); _dhwIndexes[smartVault][flushIndex.current] = flushDhwIndexes; emit SmartVaultFlushed(smartVault, flushIndex.current); flushIndex.current++; _flushIndexes[smartVault] = flushIndex; } function _getPreviousDhwIndexes(address smartVault, uint256 flushIndex) private view returns (uint16a16) { return flushIndex == 0 ? uint16a16.wrap(0) : _dhwIndexes[smartVault][flushIndex - 1]; } function _onlyRegisteredSmartVault(address smartVault) internal view { if (!_smartVaultRegistry[smartVault]) { revert SmartVaultNotRegisteredYet(); } } function _isViewExecution() private view returns (bool) { return tx.origin == address(0); } function _nonGhostVault(address[] memory strategies_) internal view { for (uint256 i; i < strategies_.length; ++i) { if (strategies_[i] != _ghostStrategy) { return; } } revert GhostVault(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (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: 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: BUSL-1.1 pragma solidity 0.8.17; import "./RequestType.sol"; /** * @notice Used when trying to set an invalid action for a smart vault. * @param address_ Address of the invalid action. */ error InvalidAction(address address_); /** * @notice Used when trying to whitelist already whitelisted action. */ error ActionStatusAlreadySet(); /** * @notice Used when trying to set actions for smart vault that already has actions set. */ error ActionsAlreadyInitialized(address smartVault); /** * @notice Too many actions have been passed when creating a vault. */ error TooManyActions(); /** * @notice Used when wrong request type is set for an action. * @param requestType Wrong request type. */ error WrongActionRequestType(RequestType requestType); /** * @notice Represents a context that is sent to actions. * @custom:member smartVault Smart vault address * @custom:member recipient In case of deposit, recipient of deposit NFT; in case of withdrawal, recipient of assets. * @custom:member executor In case of deposit, executor of deposit action; in case of withdrawal, executor of claimWithdrawal action. * @custom:member owner In case of deposit, owner of assets; in case of withdrawal, owner of withdrawal NFT. * @custom:member requestType Request type that triggered the action. * @custom:member tokens Tokens involved. * @custom:member amount Amount of tokens. */ struct ActionContext { address smartVault; address recipient; address executor; address owner; RequestType requestType; address[] tokens; uint256[] amounts; } interface IAction { /** * @notice Executes the action. * @param actionCtx Context for action execution. */ function executeAction(ActionContext calldata actionCtx) external; } interface IActionManager { /** * @notice Sets actions for a smart vault. * @dev Requirements: * - caller needs role ROLE_SMART_VAULT_INTEGRATOR * @param smartVault Smart vault for which the actions will be set. * @param actions Actions to set. * @param requestTypes Specifies for each action, which request type triggers that action. */ function setActions(address smartVault, IAction[] calldata actions, RequestType[] calldata requestTypes) external; /** * @notice Runs actions for a smart vault. * @dev Requirements: * - caller needs role ROLE_SMART_VAULT_MANAGER * @param actionCtx Execution context for the actions. */ function runActions(ActionContext calldata actionCtx) external; /** * @notice Adds or removes an action from the whitelist. * @dev Requirements: * - caller needs role ROLE_SPOOL_ADMIN * @param action Address of an action to add or remove from the whitelist. * @param whitelist If true, action will be added to the whitelist, if false, it will be removed from it. */ function whitelistAction(address action, bool whitelist) external; /** * @notice Emitted when an action is added or removed from the whitelist. * @param action Address of the action that was added or removed from the whitelist. * @param whitelisted True if it was added, false if it was removed from the whitelist. */ event ActionListed(address indexed action, bool whitelisted); /** * @notice Emitted when an action is set for a vault * @param smartVault Address of the smart vault * @param action Address of the action that was added * @param requestType Trigger for executing the action */ event ActionSet(address indexed smartVault, address indexed action, RequestType requestType); }
// 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 "./ISmartVault.sol"; import "../libraries/uint16a16Lib.sol"; /** * @notice Used when deposited assets are not the same length as underlying assets. */ error InvalidAssetLengths(); /** * @notice Used when lengths of NFT id and amount arrays when claiming NFTs don't match. */ error InvalidNftArrayLength(); /** * @notice Used when there are no pending deposits to recover. * E.g., they were already recovered or flushed. */ error NoDepositsToRecover(); /** * @notice Used when trying to recover pending deposits from a smart vault that has non-ghost strategies. */ error NotGhostVault(); /** * @notice Gathers input for depositing assets. * @custom:member smartVault Smart vault for which the deposit is made. * @custom:member assets Amounts of assets being deposited. * @custom:member receiver Receiver of the deposit NFT. * @custom:member referral Referral address. * @custom:member doFlush If true, the smart vault will be flushed after the deposit as part of same transaction. */ struct DepositBag { address smartVault; uint256[] assets; address receiver; address referral; bool doFlush; } /** * @notice Gathers extra input for depositing assets. * @custom:member depositor Address making the deposit. * @custom:member tokens Tokens of the smart vault. * @custom:member strategies Strategies of the smart vault. * @custom:member allocations Set allocation of funds between strategies. * @custom:member flushIndex Current flush index of the smart vault. */ struct DepositExtras { address depositor; address[] tokens; address[] strategies; uint16a16 allocations; uint256 flushIndex; } /** * @notice Gathers minted SVTs for a specific fee type. * @custom:member depositFees Minted SVTs for deposit fees. * @custom:member performanceFees Minted SVTs for performance fees. * @custom:member managementFees Minted SVTs for management fees. */ struct SmartVaultFeesCollected { uint256 depositFees; uint256 performanceFees; uint256 managementFees; } /** * @notice Gathers return values of syncing deposits. * @custom:member mintedSVTs Amount of SVTs minted. * @custom:member dhwTimestamp Timestamp of the last DHW synced. * @custom:member feeSVTs Amount of SVTs minted as fees. * @custom:member feesCollected Breakdown of amount of SVTs minted as fees. * @custom:member initialLockedSVTs Amount of initial locked SVTs. * @custom:member sstShares Amount of SSTs claimed for each strategy. */ struct DepositSyncResult { uint256 mintedSVTs; uint256 dhwTimestamp; uint256 feeSVTs; SmartVaultFeesCollected feesCollected; uint256 initialLockedSVTs; uint256[] sstShares; } /** * @custom:member smartVault Smart Vault address * @custom:member bag flush index, lastDhwSyncedTimestamp * @custom:member strategies strategy addresses * @custom:member assetGroup vault asset group token addresses * @custom:member dhwIndexes DHW Indexes for given flush index * @custom:member dhwIndexesOld DHW Indexes for previous flush index * @custom:member fees smart vault fee configuration * @return syncResult Result of the smart vault sync. */ struct SimulateDepositParams { address smartVault; // bag[0]: flushIndex, // bag[1]: lastDhwSyncedTimestamp, uint256[2] bag; address[] strategies; address[] assetGroup; uint16a16 dhwIndexes; uint16a16 dhwIndexesOld; SmartVaultFees fees; } interface IDepositManager { /** * @notice User redeemed deposit NFTs for SVTs * @param smartVault Smart vault address * @param claimer Claimer address * @param claimedVaultTokens Amount of SVTs claimed * @param nftIds NFTs to burn * @param nftAmounts NFT shares to burn */ event SmartVaultTokensClaimed( address indexed smartVault, address indexed claimer, uint256 claimedVaultTokens, uint256[] nftIds, uint256[] nftAmounts ); /** * @notice A deposit has been initiated * @param smartVault Smart vault address * @param receiver Beneficiary of the deposit * @param depositId Deposit NFT ID for this deposit * @param flushIndex Flush index the deposit was scheduled for * @param assets Amount of assets to deposit * @param depositor Address that initiated the deposit * @param referral Referral address */ event DepositInitiated( address indexed smartVault, address indexed receiver, uint256 indexed depositId, uint256 flushIndex, uint256[] assets, address depositor, address referral ); /** * @notice Pending deposits were recovered. * @param smartVault Smart vault address. * @param recoveredAssets Amount of assets recovered. */ event PendingDepositsRecovered(address indexed smartVault, uint256[] recoveredAssets); /** * @notice Smart vault fees collected. * @param smartVault Smart vault address. * @param smartVaultFeesCollected Collected smart vault fee amounts. */ event SmartVaultFeesMinted(address indexed smartVault, SmartVaultFeesCollected smartVaultFeesCollected); /** * @notice Simulate vault synchronization (i.e. DHW was completed, but vault wasn't synced yet) */ function syncDepositsSimulate(SimulateDepositParams calldata parameters) external view returns (DepositSyncResult memory syncResult); /** * @notice Synchronize vault deposits for completed DHW runs * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart Vault address * @param bag flushIndex, lastDhwSyncedTimestamp * @param strategies vault strategy addresses * @param dhwIndexes dhw indexes for given and previous flushIndex * @param assetGroup vault asset group token addresses * @param fees smart vault fee configuration * @return syncResult Result of the smart vault sync. */ function syncDeposits( address smartVault, uint256[2] calldata bag, // uint256 flushIndex, // uint256 lastDhwSyncedTimestamp address[] calldata strategies, uint16a16[2] calldata dhwIndexes, address[] calldata assetGroup, SmartVaultFees calldata fees ) external returns (DepositSyncResult memory syncResult); /** * @notice Adds deposits for the next flush cycle. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param bag Deposit parameters. * @param bag2 Extra parameters. * @return nftId ID of the deposit NFT. */ function depositAssets(DepositBag calldata bag, DepositExtras calldata bag2) external returns (uint256 nftId); /** * @notice Mark deposits ready to be processed in the next DHW cycle * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart Vault address * @param flushIndex index to flush * @param strategies vault strategy addresses * @param allocations vault strategy allocations * @param tokens vault asset group token addresses * @return dhwIndexes DHW indexes in which the deposits will be included */ function flushSmartVault( address smartVault, uint256 flushIndex, address[] calldata strategies, uint16a16 allocations, address[] calldata tokens ) external returns (uint16a16 dhwIndexes); /** * @notice Get the number of SVTs that are available, but haven't been claimed yet, for the given NFT * @param smartVaultAddress Smart Vault address * @param data NFT deposit NFT metadata * @param nftShares amount of NFT shares to burn for SVTs * @param mintedSVTs amount of SVTs minted for this flush * @param tokens vault asset group addresses */ function getClaimedVaultTokensPreview( address smartVaultAddress, DepositMetadata memory data, uint256 nftShares, uint256 mintedSVTs, address[] calldata tokens ) external view returns (uint256); /** * @notice Fetch assets deposited in a given vault flush */ function smartVaultDeposits(address smartVault, uint256 flushIdx, uint256 assetGroupLength) external view returns (uint256[] memory); /** * @notice Claim SVTs by burning deposit NFTs. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart Vault address * @param nftIds NFT ids to burn * @param nftAmounts NFT amounts to burn (support for partial burn) * @param tokens vault asset group token addresses * @param owner address owning NFTs * @param executor address executing the claim transaction * @param flushIndexToSync next flush index to sync for the smart vault * @return claimedTokens Amount of smart vault tokens claimed. */ function claimSmartVaultTokens( address smartVault, uint256[] calldata nftIds, uint256[] calldata nftAmounts, address[] calldata tokens, address owner, address executor, uint256 flushIndexToSync ) external returns (uint256 claimedTokens); /** * @notice Recovers pending deposits from smart vault. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param smartVault Smart vault from which to recover pending deposits. * @param flushIndex Flush index for which to recover pending deposits. * @param strategies Addresses of smart vault's strategies. * @param tokens Asset group token addresses. * @param emergencyWallet Address of emergency withdraw wallet. */ function recoverPendingDeposits( address smartVault, uint256 flushIndex, address[] calldata strategies, address[] calldata tokens, address emergencyWallet ) external; /** * @notice Gets current required deposit ratio of a smart vault. * @param tokens Asset tokens of the smart vault. * @param allocations Allocation between strategies of the smart vault. * @param strategies Strategies of the smart vault. * @return ratio Required deposit ratio of the smart vault. */ function getDepositRatio(address[] memory tokens, uint16a16 allocations, address[] memory strategies) external view returns (uint256[] memory ratio); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "./ISmartVault.sol"; import "./RequestType.sol"; error GuardsAlreadyInitialized(); error GuardsNotInitialized(); error GuardError(); /** * @notice Used when a guard fails. * @param guardNum Sequential number of the guard that failed. */ error GuardFailed(uint256 guardNum); error InvalidGuardParamType(uint256 paramType); /** * @notice Too many guard definitions have been passed when creating a vault. */ error TooManyGuards(); /** * @notice The guard definition does not have all required inputs */ error IncompleteGuardDefinition(); /** * @custom:member VaultAddress Address of the smart vault. * @custom:member Executor In case of deposit, executor of deposit action; in case of withdrawal, executor of redeem action. * @custom:member Receiver Receiver of receipt NFT. * @custom:member Owner In case of deposit, owner of assets; in case of withdrawal, owner of vault shares. * @custom:member Assets Amounts of assets involved. * @custom:member Tokens Addresses of assets involved. * @custom:member AssetGroup Asset group of the smart vault. * @custom:member CustomValue Custom value. * @custom:member DynamicCustomValue Dynamic custom value. */ enum GuardParamType { VaultAddress, Executor, Receiver, Owner, Assets, Tokens, AssetGroup, CustomValue, DynamicCustomValue } /** * @custom:member methodSignature Signature of the method to invoke * @custom:member contractAddress Address of the contract to invoke * @custom:member operator The operator to use when comparing expectedValue to guard's function result. * @custom:member expectedValue Value to use when comparing with the guard function result. * - System only supports guards with return values that can be cast to uint256. * @custom:member methodParamTypes Types of parameters that the guard function is expecting. * @custom:member methodParamValues Parameter values that will be passed into the guard function call. * - This array should only include fixed/static values. Parameters that are resolved at runtime should be omitted. * - All values should be encoded using "abi.encode" before passing them to the GuardManager contract. * - We assume that all static types are encoded to 32 bytes. Fixed-size static arrays and structs with only static * type members are not supported. * - If empty, system will assume the expected value is bool(true). */ struct GuardDefinition { string methodSignature; address contractAddress; bytes2 operator; uint256 expectedValue; GuardParamType[] methodParamTypes; bytes[] methodParamValues; } /** * @custom:member receiver Receiver of receipt NFT. * @custom:member executor In case of deposit, executor of deposit action; in case of withdrawal, executor of redeem action. * @custom:member owner In case of deposit, owner of assets; in case of withdrawal, owner of vault shares. * @custom:member requestType Request type for which the guard is run. * @custom:member assets Amounts of assets involved. * @custom:member tokens Addresses of tokens involved. */ struct RequestContext { address receiver; address executor; address owner; RequestType requestType; uint256[] assets; address[] tokens; } interface IGuardManager { /** * @notice Runs guards for a smart vault. * @dev Reverts if any guard fails. * The context.methodParamValues array should only include fixed/static values. * Parameters that are resolved at runtime should be omitted. All values should be encoded using "abi.encode" before * passing them to the GuardManager contract. We assume that all static types are encoded to 32 bytes. Fixed-size * static arrays and structs with only static type members are not supported. * @param smartVault Smart vault for which to run the guards. * @param context Context for running the guards. */ function runGuards(address smartVault, RequestContext calldata context) external view; /** * @notice Gets guards for smart vault and request type. * @param smartVault Smart vault for which to get guards. * @param requestType Request type for which to get guards. * @return guards Guards for the smart vault and request type. */ function readGuards(address smartVault, RequestType requestType) external view returns (GuardDefinition[] memory guards); /** * @notice Sets guards for the smart vault. * @dev * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_INTEGRATOR * - guards should not have been already set for the smart vault * @param smartVault Smart vault for which to set the guards. * @param guards Guards to set. Grouped by the request types. * @param requestTypes Request types for groups of guards. */ function setGuards(address smartVault, GuardDefinition[][] calldata guards, RequestType[] calldata requestTypes) external; /** * @notice Emitted when guards are set for a smart vault. * @param smartVault Smart vault for which guards were set. * @param guards Guard definitions * @param requestTypes Guard triggers */ event GuardsInitialized(address indexed smartVault, GuardDefinition[][] guards, RequestType[] requestTypes); }
// 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 "../libraries/uint16a16Lib.sol"; error InvalidRiskInputLength(); error RiskScoreValueOutOfBounds(uint8 value); error RiskToleranceValueOutOfBounds(int8 value); error CannotSetRiskScoreForGhostStrategy(uint8 riskScore); error InvalidAllocationSum(uint256 allocationsSum); error InvalidRiskScores(address riskProvider, address strategy); interface IRiskManager { /* ========== VIEW FUNCTIONS ========== */ /** * @notice Calculates allocation between strategies based on * - risk scores of strategies * - risk appetite * @param smartVault Smart vault address. * @param strategies Strategies. * @return allocation Calculated allocation. */ function calculateAllocation(address smartVault, address[] calldata strategies) external view returns (uint16a16 allocation); /** * @notice Gets risk scores for strategies. * @param riskProvider Requested risk provider. * @param strategy Strategies. * @return riskScores Risk scores for strategies. */ function getRiskScores(address riskProvider, address[] memory strategy) external view returns (uint8[] memory riskScores); /** * @notice Gets configured risk provider for a smart vault. * @param smartVault Smart vault. * @return riskProvider Risk provider for the smart vault. */ function getRiskProvider(address smartVault) external view returns (address riskProvider); /** * @notice Gets configured allocation provider for a smart vault. * @param smartVault Smart vault. * @return allocationProvider Allocation provider for the smart vault. */ function getAllocationProvider(address smartVault) external view returns (address allocationProvider); /** * @notice Gets configured risk tolerance for a smart vault. * @param smartVault Smart vault. * @return riskTolerance Risk tolerance for the smart vault. */ function getRiskTolerance(address smartVault) external view returns (int8 riskTolerance); /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /** * @notice Sets risk provider for a smart vault. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_INTEGRATOR * - risk provider must have role ROLE_RISK_PROVIDER * @param smartVault Smart vault. * @param riskProvider_ Risk provider to set. */ function setRiskProvider(address smartVault, address riskProvider_) external; /** * @notice Sets allocation provider for a smart vault. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_INTEGRATOR * - allocation provider must have role ROLE_ALLOCATION_PROVIDER * @param smartVault Smart vault. * @param allocationProvider Allocation provider to set. */ function setAllocationProvider(address smartVault, address allocationProvider) external; /** * @notice Sets risk scores for strategies. * @dev Requirements: * - caller must have role ROLE_RISK_PROVIDER * @param riskScores Risk scores to set for strategies. * @param strategies Strategies for which to set risk scores. */ function setRiskScores(uint8[] calldata riskScores, address[] calldata strategies) external; /** * @notice Sets risk tolerance for a smart vault. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_INTEGRATOR * - risk tolerance must be within valid bounds * @param smartVault Smart vault. * @param riskTolerance Risk tolerance to set. */ function setRiskTolerance(address smartVault, int8 riskTolerance) external; /** * @notice Risk scores updated * @param riskProvider risk provider address * @param strategies strategy addresses * @param riskScores risk score values */ event RiskScoresUpdated(address indexed riskProvider, address[] strategies, uint8[] riskScores); /** * @notice Smart vault risk provider set * @param smartVault Smart vault address * @param riskProvider New risk provider address */ event RiskProviderSet(address indexed smartVault, address indexed riskProvider); /** * @notice Smart vault allocation provider set * @param smartVault Smart vault address * @param allocationProvider New allocation provider address */ event AllocationProviderSet(address indexed smartVault, address indexed allocationProvider); /** * @notice Smart vault risk appetite * @param smartVault Smart vault address * @param riskTolerance risk appetite value */ event RiskToleranceSet(address indexed smartVault, int8 riskTolerance); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin-upgradeable/token/ERC1155/extensions/IERC1155MetadataURIUpgradeable.sol"; import "./Constants.sol"; import "./RequestType.sol"; /* ========== ERRORS ========== */ /** * @notice Used when the ID for deposit NFTs overflows. * @dev Should never happen. */ error DepositIdOverflow(); /** * @notice Used when the ID for withdrawal NFTs overflows. * @dev Should never happen. */ error WithdrawalIdOverflow(); /** * @notice Used when ID does not represent a deposit NFT. * @param depositNftId Invalid ID for deposit NFT. */ error InvalidDepositNftId(uint256 depositNftId); /** * @notice Used when ID does not represent a withdrawal NFT. * @param withdrawalNftId Invalid ID for withdrawal NFT. */ error InvalidWithdrawalNftId(uint256 withdrawalNftId); /** * @notice Used when balance of the NFT is invalid. * @param balance Actual balance of the NFT. */ error InvalidNftBalance(uint256 balance); /** * @notice Used when someone wants to transfer invalid NFT shares amount. * @param transferAmount Amount of shares requested to be transferred. */ error InvalidNftTransferAmount(uint256 transferAmount); /** * @notice Used when user tries to send tokens to himself. */ error SenderEqualsRecipient(); /* ========== STRUCTS ========== */ struct DepositMetadata { uint256[] assets; uint256 initiated; uint256 flushIndex; } /** * @notice Holds metadata detailing the withdrawal behind the NFT. * @custom:member vaultShares Vault shares withdrawn. * @custom:member flushIndex Flush index into which withdrawal is included. */ struct WithdrawalMetadata { uint256 vaultShares; uint256 flushIndex; } /** * @notice Holds all smart vault fee percentages. * @custom:member managementFeePct Management fee of the smart vault. * @custom:member depositFeePct Deposit fee of the smart vault. * @custom:member performanceFeePct Performance fee of the smart vault. */ struct SmartVaultFees { uint16 managementFeePct; uint16 depositFeePct; uint16 performanceFeePct; } /* ========== INTERFACES ========== */ interface ISmartVault is IERC20Upgradeable, IERC1155MetadataURIUpgradeable { /* ========== EXTERNAL VIEW FUNCTIONS ========== */ /** * @notice Fractional balance of a NFT (0 - NFT_MINTED_SHARES). * @param account Account to check the balance for. * @param id ID of the NFT to check. * @return fractionalBalance Fractional balance of account for the NFT. */ function balanceOfFractional(address account, uint256 id) external view returns (uint256 fractionalBalance); /** * @notice Fractional balance of a NFTs (0 - NFT_MINTED_SHARES). * @param account Account to check the balance for. * @param ids IDs of the NFTs to check. * @return fractionalBalances Fractional balances of account for each requested NFT. */ function balanceOfFractionalBatch(address account, uint256[] calldata ids) external view returns (uint256[] memory fractionalBalances); /** * @notice Gets the asset group used by the smart vault. * @return id ID of the asset group. */ function assetGroupId() external view returns (uint256 id); /** * @notice Gets the name of the smart vault. * @return name Name of the vault. */ function vaultName() external view returns (string memory name); /** * @notice Gets metadata for NFTs. * @param nftIds IDs of NFTs. * @return metadata Metadata for each requested NFT. */ function getMetadata(uint256[] calldata nftIds) external view returns (bytes[] memory metadata); /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /** * @notice Set a new base URI for ERC1155 metadata. * @param uri_ new base URI value */ function setBaseURI(string memory uri_) external; /** * @notice Mints smart vault tokens for receiver. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param receiver REceiver of minted tokens. * @param vaultShares Amount of tokens to mint. */ function mintVaultShares(address receiver, uint256 vaultShares) external; /** * @notice Burns smart vault tokens and releases strategy shares back to strategies. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param owner Address for which to burn the tokens. * @param vaultShares Amount of tokens to burn. * @param strategies Strategies for which to release the strategy shares. * @param shares Amounts of strategy shares to release. */ function burnVaultShares( address owner, uint256 vaultShares, address[] calldata strategies, uint256[] calldata shares ) external; /** * @notice Mints a new withdrawal NFT. * @dev Supply of minted NFT is NFT_MINTED_SHARES (for partial burning). * Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param receiver Address that will receive the NFT. * @param metadata Metadata to store for minted NFT. * @return id ID of the minted NFT. */ function mintWithdrawalNFT(address receiver, WithdrawalMetadata calldata metadata) external returns (uint256 id); /** * @notice Mints a new deposit NFT. * @dev Supply of minted NFT is NFT_MINTED_SHARES (for partial burning). * Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param receiver Address that will receive the NFT. * @param metadata Metadata to store for minted NFT. * @return id ID of the minted NFT. */ function mintDepositNFT(address receiver, DepositMetadata calldata metadata) external returns (uint256 id); /** * @notice Burns NFTs and returns their metadata. * Allows for partial burning. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param owner Owner of NFTs to burn. * @param nftIds IDs of NFTs to burn. * @param nftAmounts NFT shares to burn (partial burn). * @return metadata Metadata for each burned NFT. */ function burnNFTs(address owner, uint256[] calldata nftIds, uint256[] calldata nftAmounts) external returns (bytes[] memory metadata); /** * @notice Transfers smart vault tokens. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param from Spender and owner of tokens. * @param to Address to which tokens will be transferred. * @param amount Amount of tokens to transfer. * @return success True if transfer was successful. */ function transferFromSpender(address from, address to, uint256 amount) external returns (bool success); /** * @notice Transfers unclaimed shares to claimer. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_MANAGER * @param claimer Address that claims the shares. * @param amount Amount of shares to transfer. */ function claimShares(address claimer, uint256 amount) external; /// @notice Emitted when base URI is changed. event BaseURIChanged(string baseUri); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; import "./IDepositManager.sol"; import "./ISmartVault.sol"; import "./ISwapper.sol"; import "./IWithdrawalManager.sol"; /* ========== ERRORS ========== */ /** * @notice Used when user has insufficient balance for redeemal of shares. */ error InsufficientBalance(uint256 available, uint256 required); /** * @notice Used when there is nothing to flush. */ error NothingToFlush(); /** * @notice Used when trying to register a smart vault that was already registered. */ error SmartVaultAlreadyRegistered(); /** * @notice Used when trying to perform an action for smart vault that was not registered yet. */ error SmartVaultNotRegisteredYet(); /** * @notice Used when user tries to configure a vault with too large management fee. */ error ManagementFeeTooLarge(uint256 mgmtFeePct); /** * @notice Used when user tries to configure a vault with too large performance fee. */ error PerformanceFeeTooLarge(uint256 performanceFeePct); /** * @notice Used when smart vault in reallocation has statically set allocation. */ error StaticAllocationSmartVault(); /** * @notice Used when user tries to configure a vault with too large deposit fee. */ error DepositFeeTooLarge(uint256 depositFeePct); /** * @notice Used when user tries redeem on behalf of another user, but the vault does not support it */ error RedeemForNotAllowed(); /** * @notice Used when trying to flush a vault that still needs to be synced. */ error VaultNotSynced(); /** * @notice Used when trying to deposit into, redeem from, or flush a smart vault that has only ghost strategies. */ error GhostVault(); /** * @notice Used when reallocation is called with expired parameters. */ error ReallocationParametersExpired(); /* ========== STRUCTS ========== */ /** * @notice Struct holding all data for registration of smart vault. * @custom:member assetGroupId Underlying asset group of the smart vault. * @custom:member strategies Strategies used by the smart vault. * @custom:member strategyAllocation Optional. If empty array, values will be calculated on the spot. * @custom:member managementFeePct Management fee of the smart vault. * @custom:member depositFeePct Deposit fee of the smart vault. * @custom:member performanceFeePct Performance fee of the smart vault. */ struct SmartVaultRegistrationForm { uint256 assetGroupId; address[] strategies; uint16a16 strategyAllocation; uint16 managementFeePct; uint16 depositFeePct; uint16 performanceFeePct; } /** * @notice Parameters for reallocation. * @custom:member smartVaults Smart vaults to reallocate. * @custom:member strategies Set of strategies involved in the reallocation. Should not include ghost strategy, even if some smart vault uses it. * @custom:member swapInfo Information for swapping assets before depositing into the protocol. * @custom:member depositSlippages Slippages used to constrain depositing into the protocol. * @custom:member withdrawalSlippages Slippages used to contrain withdrawal from the protocol. * @custom:member exchangeRateSlippages Slippages used to constratrain exchange rates for asset tokens. * @custom:member validUntil Sets the maximum timestamp the user is willing to wait to start executing reallocation. */ struct ReallocateParamBag { address[] smartVaults; address[] strategies; SwapInfo[][] swapInfo; uint256[][] depositSlippages; uint256[][] withdrawalSlippages; uint256[2][] exchangeRateSlippages; uint256 validUntil; } struct FlushIndex { uint128 current; uint128 toSync; } /* ========== INTERFACES ========== */ interface ISmartVaultRegistry { /** * @notice Registers smart vault into the Spool protocol. * @dev Requirements: * - caller must have role ROLE_SMART_VAULT_INTEGRATOR * @param smartVault Smart vault to register. * @param registrationForm Form with information for registration. */ function registerSmartVault(address smartVault, SmartVaultRegistrationForm calldata registrationForm) external; } interface ISmartVaultManager is ISmartVaultRegistry { /* ========== EXTERNAL VIEW FUNCTIONS ========== */ /** * @notice Gets do-hard-work indexes. * @param smartVault Smart vault. * @param flushIndex Flush index. * @return dhwIndexes Do-hard-work indexes for flush index of the smart vault. */ function dhwIndexes(address smartVault, uint256 flushIndex) external view returns (uint16a16 dhwIndexes); /** * @notice Gets latest flush index for a smart vault. * @param smartVault Smart vault. * @return flushIndex Latest flush index for the smart vault. */ function getLatestFlushIndex(address smartVault) external view returns (uint256 flushIndex); /** * @notice Gets strategy allocation for a smart vault. * @param smartVault Smart vault. * @return allocation Strategy allocation for the smart vault. */ function allocations(address smartVault) external view returns (uint16a16 allocation); /** * @notice Gets strategies used by a smart vault. * @param smartVault Smart vault. * @return strategies Strategies for the smart vault. */ function strategies(address smartVault) external view returns (address[] memory strategies); /** * @notice Gets asest group used by a smart vault. * @param smartVault Smart vault. * @return assetGroupId ID of the asset group used by the smart vault. */ function assetGroupId(address smartVault) external view returns (uint256 assetGroupId); /** * @notice Gets required deposit ratio for a smart vault. * @param smartVault Smart vault. * @return ratio Required deposit ratio for the smart vault. */ function depositRatio(address smartVault) external view returns (uint256[] memory ratio); /* ========== EXTERNAL MUTATIVE FUNCTIONS ========== */ /** * @notice Flushes deposits and withdrawal for the next do-hard-work. * @param smartVault Smart vault to flush. */ function flushSmartVault(address smartVault) external; /** * @notice Reallocates smart vaults. * @dev Requirements: * - caller must have a ROLE_REALLOCATOR role * - smart vaults must be registered * - smart vaults must use same asset group * - strategies must represent a set of strategies used by smart vaults * @param reallocateParams Paramaters for reallocation. */ function reallocate(ReallocateParamBag calldata reallocateParams) external; /** * @notice Removes strategy from vaults, and optionally removes it from the system as well. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * - the strategy has to be active (requires ROLE_STRATEGY) * @param strategy Strategy address to remove. * @param vaults Array of vaults from which to remove the strategy * @param disableStrategy Also disable the strategy across the system */ function removeStrategyFromVaults(address strategy, address[] calldata vaults, bool disableStrategy) external; /** * @notice Syncs smart vault with strategies. * @param smartVault Smart vault to sync. * @param revertIfError If true, sync will revert if every flush index cannot be synced; if false it will sync all flush indexes it can. */ function syncSmartVault(address smartVault, bool revertIfError) external; /** * @dev Calculate number of SVTs that haven't been synced yet after DHW runs * DHW has minted strategy shares, but vaults haven't claimed them yet. * Includes management fees (percentage of assets under management, distributed throughout a year) and deposit fees . * Invariants: * - There can't be more than once un-synced flush index per vault at any given time. * - Flush index can't be synced, if all DHWs haven't been completed yet. * * Can be used to retrieve the number of SSTs the vault would claim during sync. * @param smartVault SmartVault address * @return oldTotalSVTs Amount of SVTs before sync * @return mintedSVTs Amount of SVTs minted during sync * @return feeSVTs Amount of SVTs pertaining to fees * @return sstShares Amount of SSTs claimed per strategy */ function simulateSync(address smartVault) external view returns (uint256 oldTotalSVTs, uint256 mintedSVTs, uint256 feeSVTs, uint256[] calldata sstShares); /** * @dev Simulate sync when burning dNFTs and return their svts value. * * @param smartVault SmartVault address * @param userAddress User address that owns dNFTs * @param nftIds Ids of dNFTs * @return svts Amount of svts user would get if he burns dNFTs */ function simulateSyncWithBurn(address smartVault, address userAddress, uint256[] calldata nftIds) external view returns (uint256 svts); /** * @notice Instantly redeems smart vault shares for assets. * @param bag Parameters for fast redeemal. * @param withdrawalSlippages Slippages guarding redeemal. * @return withdrawnAssets Amount of assets withdrawn. */ function redeemFast(RedeemBag calldata bag, uint256[][] calldata withdrawalSlippages) external returns (uint256[] memory withdrawnAssets); /** * @notice Simulates redeem fast of smart vault shares. * @dev Should only be run by address zero to simulate the redeemal and parse logs. * @param bag Parameters for fast redeemal. * @param withdrawalSlippages Slippages guarding redeemal. * @param redeemer Address of a user to simulate redeem for. * @return withdrawnAssets Amount of assets withdrawn. */ function redeemFastView(RedeemBag calldata bag, uint256[][] calldata withdrawalSlippages, address redeemer) external returns (uint256[] memory withdrawnAssets); /** * @notice Claims withdrawal of assets by burning withdrawal NFT. * @dev Requirements: * - withdrawal NFT must be valid * @param smartVault Address of the smart vault that issued the withdrawal NFT. * @param nftIds ID of withdrawal NFT to burn. * @param nftAmounts amounts * @param receiver Receiver of claimed assets. * @return assetAmounts Amounts of assets claimed. * @return assetGroupId ID of the asset group. */ function claimWithdrawal( address smartVault, uint256[] calldata nftIds, uint256[] calldata nftAmounts, address receiver ) external returns (uint256[] memory assetAmounts, uint256 assetGroupId); /** * @notice Claims smart vault tokens by burning the deposit NFT. * @dev Requirements: * - deposit NFT must be valid * - flush must be synced * @param smartVaultAddress Address of the smart vault that issued the deposit NFT. * @param nftIds ID of the deposit NFT to burn. * @param nftAmounts amounts * @return claimedAmount Amount of smart vault tokens claimed. */ function claimSmartVaultTokens(address smartVaultAddress, uint256[] calldata nftIds, uint256[] calldata nftAmounts) external returns (uint256 claimedAmount); /** * @notice Initiates a withdrawal process and mints a withdrawal NFT. Once all DHWs are executed, user can * use the withdrawal NFT to claim the assets. * Optionally, caller can pass a list of deposit NFTs to unwrap. * @param bag smart vault address, amount of shares to redeem, nft ids and amounts to burn * @param receiver address that will receive the withdrawal NFT * @param doFlush optionally flush the smart vault * @return receipt ID of the receipt withdrawal NFT. */ function redeem(RedeemBag calldata bag, address receiver, bool doFlush) external returns (uint256 receipt); /** * @notice Initiates a withdrawal process and mints a withdrawal NFT. Once all DHWs are executed, user can * use the withdrawal NFT to claim the assets. * Optionally, caller can pass a list of deposit NFTs to unwrap. * @param bag smart vault address, amount of shares to redeem, nft ids and amounts to burn * @param owner address that owns the shares to be redeemed and will receive the withdrawal NFT * @param doFlush optionally flush the smart vault * @return receipt ID of the receipt withdrawal NFT. */ function redeemFor(RedeemBag calldata bag, address owner, bool doFlush) external returns (uint256 receipt); /** * @notice Initiated a deposit and mints a deposit NFT. Once all DHWs are executed, user can * unwrap the deposit NDF and claim his SVTs. * @param bag smartVault address, assets, NFT receiver address, referral address, doFlush * @return receipt ID of the receipt deposit NFT. */ function deposit(DepositBag calldata bag) external returns (uint256 receipt); /** * @notice Recovers pending deposits from smart vault to emergency wallet. * @dev Requirements: * - caller must have role ROLE_SPOOL_ADMIN * - all strategies of the smart vault need to be ghost strategies * @param smartVault Smart vault from which to recover pending deposits. */ function recoverPendingDeposits(address smartVault) external; /* ========== EVENTS ========== */ /** * @notice Smart vault has been flushed * @param smartVault Smart vault address * @param flushIndex Flush index */ event SmartVaultFlushed(address indexed smartVault, uint256 flushIndex); /** * @notice Smart vault has been synced * @param smartVault Smart vault address * @param flushIndex Flush index */ event SmartVaultSynced(address indexed smartVault, uint256 flushIndex); /** * @notice Smart vault has been registered * @param smartVault Smart vault address * @param registrationForm Smart vault configuration */ event SmartVaultRegistered(address indexed smartVault, SmartVaultRegistrationForm registrationForm); /** * @notice Strategy was removed from the vault * @param strategy Strategy address * @param vault Vault to remove the strategy from */ event StrategyRemovedFromVault(address indexed strategy, address indexed vault); /** * @notice Vault was reallocation executed * @param smartVault Smart vault address * @param newAllocations new vault strategy allocations */ event SmartVaultReallocated(address indexed smartVault, uint16a16 newAllocations); }
// 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; /// @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: BUSL-1.1 pragma solidity 0.8.17; import "../libraries/uint16a16Lib.sol"; /** * @notice Used when trying to burn withdrawal NFT that was not synced yet. * @param id ID of the NFT. */ error WithdrawalNftNotSyncedYet(uint256 id); /** * @notice Base information for redeemal. * @custom:member smartVault Smart vault from which to redeem. * @custom:member shares Amount of smart vault shares to redeem. * @custom:member nftIds IDs of deposit NFTs to burn before redeemal. * @custom:member nftAmounts Amounts of NFT shares to burn. */ struct RedeemBag { address smartVault; uint256 shares; uint256[] nftIds; uint256[] nftAmounts; } /** * @notice Extra information for fast redeemal. * @custom:member strategies Strategies of the smart vault. * @custom:member assetGroup Asset group of the smart vault. * @custom:member assetGroupId ID of the asset group of the smart vault. * @custom:member redeemer Address that initiated the redeemal. * @custom:member withdrawalSlippages Slippages used to guard redeemal. */ struct RedeemFastExtras { address[] strategies; address[] assetGroup; uint256 assetGroupId; address redeemer; uint256[][] withdrawalSlippages; } /** * @notice Extra information for redeemal. * @custom:member receiver Receiver of the withdraw NFT. * @custom:member owner Address that owns the shares being redeemed. * @custom:member executor Address that initiated the redeemal. * @custom:member flushIndex Current flush index of the smart vault. */ struct RedeemExtras { address receiver; address owner; address executor; uint256 flushIndex; } /** * @notice Information used to claim withdrawal. * @custom:member smartVault Smart vault from which to claim withdrawal. * @custom:member nftIds Withdrawal NFTs to burn while claiming withdrawal. * @custom:member nftAmounts Amounts of NFT shares to burn. * @custom:member receiver Receiver of withdrawn assets. * @custom:member executor Address that initiated the withdrawal claim. * @custom:member assetGroupId ID of the asset group of the smart vault. * @custom:member assetGroup Asset group of the smart vault. * @custom:member flushIndexToSync Next flush index to sync for the smart vault. */ struct WithdrawalClaimBag { address smartVault; uint256[] nftIds; uint256[] nftAmounts; address receiver; address executor; uint256 assetGroupId; address[] assetGroup; uint256 flushIndexToSync; } interface IWithdrawalManager { /** * @notice User redeemed withdrawal NFTs for underlying assets * @param smartVault Smart vault address * @param claimer Claimer address * @param nftIds NFTs to burn * @param nftAmounts NFT shares to burn * @param withdrawnAssets Amount of underlying assets withdrawn */ event WithdrawalClaimed( address indexed smartVault, address indexed claimer, uint256 assetGroupId, uint256[] nftIds, uint256[] nftAmounts, uint256[] withdrawnAssets ); /** * @notice A deposit has been initiated * @param smartVault Smart vault address * @param owner Owner of shares to be redeemed * @param redeemId Withdrawal NFT ID for this redeemal * @param flushIndex Flush index the redeem was scheduled for * @param shares Amount of vault shares to redeem * @param receiver Beneficiary that will be able to claim the underlying assets */ event RedeemInitiated( address indexed smartVault, address indexed owner, uint256 indexed redeemId, uint256 flushIndex, uint256 shares, address receiver ); /** * @notice A deposit has been initiated * @param smartVault Smart vault address * @param redeemer Redeem initiator and owner of shares * @param shares Amount of vault shares to redeem * @param nftIds NFTs to burn * @param nftAmounts NFT shares to burn * @param assetsWithdrawn Amount of underlying assets withdrawn */ event FastRedeemInitiated( address indexed smartVault, address indexed redeemer, uint256 shares, uint256[] nftIds, uint256[] nftAmounts, uint256[] assetsWithdrawn ); /** * @notice Flushes smart vaults deposits and withdrawals to the strategies. * @dev Requirements: * - can only be called by user granted ROLE_SMART_VAULT_MANAGER * @param smartVault Smart vault to flush. * @param flushIndex Current flush index of the smart vault. * @param strategies Strategies of the smart vault. * @return dhwIndexes current do-hard-work indexes of the strategies. */ function flushSmartVault(address smartVault, uint256 flushIndex, address[] calldata strategies) external returns (uint16a16 dhwIndexes); /** * @notice Claims withdrawal. * @dev Requirements: * - can only be called by user granted ROLE_SMART_VAULT_MANAGER * @param bag Parameters for claiming withdrawal. * @return withdrawnAssets Amount of assets withdrawn. * @return assetGroupId ID of the asset group. */ function claimWithdrawal(WithdrawalClaimBag calldata bag) external returns (uint256[] memory withdrawnAssets, uint256 assetGroupId); /** * @notice Syncs withdrawals between strategies and smart vault after do-hard-works. * @dev Requirements: * - can only be called by user granted ROLE_SMART_VAULT_MANAGER * @param smartVault Smart vault to sync. * @param flushIndex Smart vault's flush index to sync. * @param strategies Strategies of the smart vault. * @param dhwIndexes_ Strategies' do-hard-work indexes to sync. */ function syncWithdrawals( address smartVault, uint256 flushIndex, address[] calldata strategies, uint16a16 dhwIndexes_ ) external; /** * @notice Redeems smart vault shares. * @dev Requirements: * - can only be called by user granted ROLE_SMART_VAULT_MANAGER * @param bag Base information for redeemal. * @param bag2 Extra information for redeemal. * @return nftId ID of the withdrawal NFT. */ function redeem(RedeemBag calldata bag, RedeemExtras calldata bag2) external returns (uint256 nftId); /** * @notice Instantly redeems smart vault shares. * @dev Requirements: * - can only be called by user granted ROLE_SMART_VAULT_MANAGER * @param bag Base information for redeemal. * @param bag Extra information for fast redeemal. * @return assets Amount of assets withdrawn. */ function redeemFast(RedeemBag calldata bag, RedeemFastExtras memory bag2) external returns (uint256[] memory assets); }
// 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; /// @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; /** * @notice Different request types for guards and actions. * @custom:member Deposit User is depositing into a smart vault. * @custom:member Withdrawal User is requesting withdrawal from a smart vault. * @custom:member TransferNFT User is transfering deposit or withdrawal NFT. * @custom:member BurnNFT User is burning deposit or withdrawal NFT. * @custom:member TransferSVTs User is transferring smart vault tokens. */ enum RequestType { Deposit, Withdrawal, TransferNFT, BurnNFT, TransferSVTs }
// 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: BUSL-1.1 pragma solidity 0.8.17; library ArrayMappingUint256 { /** * @notice Map mapping(uint256 => uint256)) values to an array. */ function toArray(mapping(uint256 => uint256) storage _self, uint256 length) external view returns (uint256[] memory) { uint256[] memory arrayOut = new uint256[](length); for (uint256 i; i < length; ++i) { arrayOut[i] = _self[i]; } return arrayOut; } /** * @notice Set array values to mapping slots. */ function setValues(mapping(uint256 => uint256) storage _self, uint256[] calldata values) external { for (uint256 i; i < values.length; ++i) { _self[i] = values[i]; } } } library ArrayMappingAddress { /** * @notice Map mapping(uint256 => address)) values to an array. */ function toArray(mapping(uint256 => address) storage _self, uint256 length) external view returns (address[] memory) { address[] memory arrayOut = new address[](length); for (uint256 i; i < length; ++i) { arrayOut[i] = _self[i]; } return arrayOut; } /** * @notice Set array values to mapping slots. */ function setValues(mapping(uint256 => address) storage _self, address[] calldata values) external { for (uint256 i; i < values.length; ++i) { _self[i] = values[i]; } } }
// 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 "../interfaces/IAssetGroupRegistry.sol"; import "../interfaces/IMasterWallet.sol"; import "../interfaces/IUsdPriceFeedManager.sol"; import "../interfaces/CommonErrors.sol"; import "./ArrayMapping.sol"; import "./SpoolUtils.sol"; import "./uint16a16Lib.sol"; /* ========== ERRORS ========== */ /** * @notice Used when strategies provided for reallocation are invalid. */ error InvalidStrategies(); /* ========== STRUCTS ========== */ /** * @notice Parameters for reallocation. * @custom:member assetGroupRegistry Asset group registry contract. * @custom:member priceFeedManager Price feed manager contract. * @custom:member masterWallet Master wallet contract. * @custom:member assetGroupId ID of the asset group used by strategies being reallocated. * @custom:member swapInfo Information for swapping assets before depositing into the protocol. * @custom:member depositSlippages Slippages used to constrain depositing into the protocol. * @custom:member withdrawalSlippages Slippages used to contrain withdrawal from the protocol. * @custom:member exchangeRateSlippages Slippages used to constratrain exchange rates for asset tokens. */ struct ReallocationParameterBag { IAssetGroupRegistry assetGroupRegistry; IUsdPriceFeedManager priceFeedManager; IMasterWallet masterWallet; uint256 assetGroupId; SwapInfo[][] swapInfo; uint256[][] depositSlippages; uint256[][] withdrawalSlippages; uint256[2][] exchangeRateSlippages; } /** * @dev Parameters for calculateReallocation. * @custom:member smartVault Smart vault. * @custom:member strategyMapping Mapping between smart vault's strategies and provided strategies. * @custom:member totalSharesToRedeem How must shares each strategy needs to redeem. Will be updated. * @custom:member strategyValues Current USD value of each strategy. */ struct CalculateReallocationParams { address smartVault; uint256[] strategyMapping; uint256[] totalSharesToRedeem; uint256[] strategyValues; } // *Ghost strategies:* // Ghost strategy should not be provided among the set of strategies, even if // some smart vault is using it. // Ghost strategies only need to be handled in one point of the library, i.e., // in the `mapStrategies`. There it index uint256 max, event though that is not // used anywhere else in the code. Other parts of the code skip the ghost // strategy either because its allocation does not change (it is always 0), or // because it is not provided in the set of all strategies. library ReallocationLib { using uint16a16Lib for uint16a16; using ArrayMappingUint256 for mapping(uint256 => uint256); using ArrayMappingAddress for mapping(uint256 => address); /** * @notice Reallocates smart vaults. * @param smartVaults Smart vaults to reallocate. * @param strategies Set of strategies involved in the reallocation. Should not include ghost strategy. * @param ghostStrategy Address of ghost strategy. * @param reallocationParams Bag with reallocation parameters. * @param _smartVaultStrategies Strategies per smart vault. * @param _smartVaultAllocations Allocations per smart vault. */ function reallocate( address[] calldata smartVaults, address[] calldata strategies, address ghostStrategy, ReallocationParameterBag calldata reallocationParams, mapping(address => address[]) storage _smartVaultStrategies, mapping(address => uint16a16) storage _smartVaultAllocations ) external { // Validate provided strategies and create a per smart vault strategy mapping. uint256[][] memory strategyMapping = _mapStrategies(smartVaults, strategies, ghostStrategy, _smartVaultStrategies); // Get current value of strategies. (uint256[] memory strategyValues, address[] memory assetGroup, uint256[] memory exchangeRates) = _valuateStrategies(strategies, reallocationParams); // Calculate reallocations needed for smart vaults. // - will hold value to move between strategies of smart vaults uint256[][][] memory reallocations = new uint256[][][](smartVaults.length); // - will hold total shares to redeem by each strategy uint256[] memory totalSharesToRedeem = new uint256[](strategies.length); for (uint256 i; i < smartVaults.length; ++i) { address smartVault = smartVaults[i]; reallocations[i] = _calculateReallocation( CalculateReallocationParams({ smartVault: smartVault, strategyMapping: strategyMapping[i], totalSharesToRedeem: totalSharesToRedeem, strategyValues: strategyValues }), _smartVaultStrategies[smartVault], _smartVaultAllocations ); } // Build the strategy-to-strategy reallocation table. uint256[][][] memory reallocationTable = _buildReallocationTable(strategyMapping, strategies.length, reallocations, totalSharesToRedeem); // Do the actual reallocation with withdrawals from and deposits into the underlying protocols. _doReallocation(strategies, reallocationParams, assetGroup, exchangeRates, reallocationTable); // Smart vaults claim strategy shares. _claimShares(smartVaults, strategies, strategyMapping, reallocationTable, reallocations); } /** * @dev Creates a mapping between strategies of smart vaults and provided strategies. * Also validates that provided strategies form a set of smart vaults' strategies. * @param smartVaults Smart vaults to reallocate. * @param strategies Set of strategies involved in the reallocation. * @param ghostStrategy Address of ghost strategy. * @param _smartVaultStrategies Strategies per smart vault. * @return Mapping between smart vault's strategies and provided strategies: * - first index runs over smart vaults * - second index runs over strategies of the smart vault. * - value represents a position of smart vault's strategy in the provided `strategies` array. * A uint256 max represents a ghost strategy, although it is not needed anywhere. */ function _mapStrategies( address[] calldata smartVaults, address[] calldata strategies, address ghostStrategy, mapping(address => address[]) storage _smartVaultStrategies ) private view returns (uint256[][] memory) { // We want to validate that the provided strategies represent a set of all the // strategies used by the smart vaults being reallocated. At the same time we // also build a mapping between strategies as listed on each smart vault and // the provided strategies. bool[] memory strategyMatched = new bool[](strategies.length); uint256[][] memory strategyMapping = new uint256[][](smartVaults.length); // Build a mapping for each smart vault and validate that all strategies are // present in the provided list. for (uint256 i; i < smartVaults.length; ++i) { // Get strategies for this smart vault. address[] storage smartVaultStrategies = _smartVaultStrategies[smartVaults[i]]; uint256 smartVaultStrategiesLength = smartVaultStrategies.length; // Mapping from this smart vault's strategies to provided strategies. strategyMapping[i] = new uint256[](smartVaultStrategiesLength); // Loop over smart vault's strategies. for (uint256 j; j < smartVaultStrategiesLength; ++j) { address strategy = smartVaultStrategies[j]; // handle ghost strategies if (strategy == ghostStrategy) { strategyMapping[i][j] = type(uint256).max; continue; } bool found = false; // Try to find the strategy in the provided list of strategies. for (uint256 k; k < strategies.length; ++k) { if (strategies[k] == strategy) { // Match found. found = true; strategyMatched[k] = true; // Add entry to the strategy mapping. strategyMapping[i][j] = k; break; } } if (!found) { // If a smart vault's strategy was not found in the provided list // of strategies, this means that the provided list is invalid. revert InvalidStrategies(); } } } // Validate that each strategy in the provided list was matched at least once. for (uint256 i; i < strategyMatched.length; ++i) { if (!strategyMatched[i]) { // If a strategy was not matched, this means that it is not used by any // smart vault and should not be included in the list. revert InvalidStrategies(); } } return strategyMapping; } /** * @dev Get current value of strategies. * @param strategies Set of strategies involved in the reallocation. * @param reallocationParams Bag with reallocation parameters. * @return strategyValues USD value of each strategy. * @return assetGroup Address of tokens in asset group. * @return exchangeRates USD exchange rate for tokens in asset group. */ function _valuateStrategies(address[] calldata strategies, ReallocationParameterBag calldata reallocationParams) private returns (uint256[] memory strategyValues, address[] memory assetGroup, uint256[] memory exchangeRates) { // Get asset group and corresponding exchange rates. assetGroup = reallocationParams.assetGroupRegistry.listAssetGroup(reallocationParams.assetGroupId); if (assetGroup.length != reallocationParams.exchangeRateSlippages.length) { revert InvalidArrayLength(); } exchangeRates = SpoolUtils.getExchangeRates(assetGroup, reallocationParams.priceFeedManager); unchecked { for (uint256 i; i < assetGroup.length; ++i) { if ( exchangeRates[i] < reallocationParams.exchangeRateSlippages[i][0] || exchangeRates[i] > reallocationParams.exchangeRateSlippages[i][1] ) { revert ExchangeRateOutOfSlippages(); } } } // Get value of each strategy. strategyValues = new uint256[](strategies.length); unchecked { for (uint256 i; i < strategyValues.length; ++i) { strategyValues[i] = IStrategy(strategies[i]).getUsdWorth(exchangeRates, reallocationParams.priceFeedManager); } } return (strategyValues, assetGroup, exchangeRates); } /** * @dev Calculates reallocation needed per smart vault. * Also updates the `totalSharesToRedeem`. * @param params Parameters for calculate reallocation. * @param smartVaultStrategies Strategies of the smart vault. * @param _smartVaultAllocations Allocations per smart vault. * @return Reallocation of the smart vault: * - first index is 0 or 1 * - 0: * - second index runs over smart vault's strategies * - value is USD value that needs to be withdrawn from the strategy * - 1: * - second index runs over smart vault's strategies + extra field * - value is USD value that needs to be deposited into the strategy * - extra field gathers total value that needs to be deposited by the smart vault */ function _calculateReallocation( CalculateReallocationParams memory params, address[] storage smartVaultStrategies, mapping(address => uint16a16) storage _smartVaultAllocations ) private returns (uint256[][] memory) { // Store length of strategies array to not read storage every time. uint256 smartVaultStrategiesLength = smartVaultStrategies.length; // Initialize array for this smart vault. uint256[][] memory reallocation = new uint256[][](2); reallocation[0] = new uint256[](smartVaultStrategiesLength); // values to redeem reallocation[1] = new uint256[](smartVaultStrategiesLength + 1); // values to deposit | total value to deposit // Get smart vaults total USD value. uint256 totalUsdValue; { uint256 totalSupply; for (uint256 i; i < smartVaultStrategiesLength; ++i) { totalSupply = IStrategy(smartVaultStrategies[i]).totalSupply(); if (totalSupply > 0) { totalUsdValue += params.strategyValues[params.strategyMapping[i]] * IStrategy(smartVaultStrategies[i]).balanceOf(params.smartVault) / totalSupply; } } } // Get sum total of target allocation. uint256 totalTargetAllocation; for (uint256 i; i < smartVaultStrategiesLength; ++i) { totalTargetAllocation += _smartVaultAllocations[params.smartVault].get(i); } // Compare target and current allocation. for (uint256 i; i < smartVaultStrategiesLength; ++i) { uint256 targetValue = _smartVaultAllocations[params.smartVault].get(i); targetValue = targetValue * totalUsdValue / totalTargetAllocation; // Get current allocation. uint256 currentValue; { uint256 totalSupply = IStrategy(smartVaultStrategies[i]).totalSupply(); if (totalSupply > 0) { currentValue = params.strategyValues[params.strategyMapping[i]] * IStrategy(smartVaultStrategies[i]).balanceOf(params.smartVault) / totalSupply; } } if (targetValue > currentValue) { // This strategy needs deposit. reallocation[1][i] = targetValue - currentValue; reallocation[1][smartVaultStrategiesLength] += targetValue - currentValue; } else if (targetValue < currentValue) { // This strategy needs withdrawal. // Relese strategy shares. uint256 sharesToRedeem = IStrategy(smartVaultStrategies[i]).balanceOf(params.smartVault); sharesToRedeem = sharesToRedeem * (currentValue - targetValue) / currentValue; IStrategy(smartVaultStrategies[i]).releaseShares(params.smartVault, sharesToRedeem); // Recalculate value to withdraw based on released shares. reallocation[0][i] = params.strategyValues[params.strategyMapping[i]] * sharesToRedeem / IStrategy(smartVaultStrategies[i]).totalSupply(); // Update total shares to redeem for strategy. params.totalSharesToRedeem[params.strategyMapping[i]] += sharesToRedeem; } } return reallocation; } /** * @dev Builds reallocation table from smart vaults' reallocations. * @param strategyMapping Mapping between smart vault's strategies and provided strategies. * @param numStrategies Number of all strategies involved in the reallocation. * @param reallocations Reallocations needed by each smart vaults. * @param totalSharesToRedeem How must shares each strategy needs to redeem. * @return Reallocation table: * - first index runs over all strategies i * - second index runs over all strategies j * - third index is 0, 1 or 2 * - 0: * - value of off-diagonal elements represent USD value that should be withdrawn by strategy i and deposited into strategy j * - value of diagonal elements represents total shares to redeem by strategy i * - 1: value is not used yet * - will be used to represent amount of matched shares from strategy j that are distributed to strategy i * - 2: value is not used yet * - will be used to represent amount of unmatched shares from strategy j that are distributed to strategy i */ function _buildReallocationTable( uint256[][] memory strategyMapping, uint256 numStrategies, uint256[][][] memory reallocations, uint256[] memory totalSharesToRedeem ) private pure returns (uint256[][][] memory) { // We want to build a reallocation table which specifies how to redistribute // funds from one strategy to another. // Reallocation table is numStrategies x numStrategies big. // A value of cell (i, j) V_ij specifies the value V that needs to be withdrawn // from strategy i and deposited into strategy j. uint256[][][] memory reallocationTable = new uint256[][][](numStrategies); for (uint256 i; i < numStrategies; ++i) { reallocationTable[i] = new uint256[][](numStrategies); for (uint256 j; j < numStrategies; ++j) { reallocationTable[i][j] = new uint256[](3); } } // Loop over smart vaults. for (uint256 i; i < reallocations.length; ++i) { // Calculate witdrawals and deposits needed to allign with new allocation. uint256 strategiesLength = reallocations[i][0].length; // Find strategies that need withdrawal. for (uint256 j; j < strategiesLength; ++j) { if (reallocations[i][0][j] == 0) { continue; } uint256[] memory values = new uint256[](2); values[0] = reallocations[i][0][j]; values[1] = reallocations[i][1][strategiesLength]; // Find strategies that need deposit. for (uint256 k; k < strategiesLength; ++k) { if (reallocations[i][1][k] == 0) { continue; } // Find value from j that should move to strategy k. uint256 valueToDeposit = values[0] * reallocations[i][1][k] / values[1]; reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][0] += valueToDeposit; values[0] -= valueToDeposit; // dust-less calculation. values[1] -= reallocations[i][1][k]; } } } // Loop over strategies. for (uint256 i; i < numStrategies; ++i) { // Set diagonal item to total shares the strategy should redeem. reallocationTable[i][i][0] = totalSharesToRedeem[i]; } return reallocationTable; } /** * @dev Does the actual reallocation by withdrawing from and depositing into strategies. * Also populates the reallocation table. * @param strategies Set of strategies involved in the reallocation. * @param reallocationParams Bag with reallocation parameters. * @param assetGroup Addresses of tokens in asset group. * @param exchangeRates Exchange rate of tokens. * @param reallocationTable Reallocation table. */ function _doReallocation( address[] calldata strategies, ReallocationParameterBag calldata reallocationParams, address[] memory assetGroup, uint256[] memory exchangeRates, uint256[][][] memory reallocationTable ) private { // Will store how much assets each strategy has to deposit. uint256[][] memory toDeposit = new uint256[][](strategies.length); for (uint256 i; i < strategies.length; ++i) { toDeposit[i] = new uint256[](assetGroup.length + 1); // toDeposit[0..strategies.length-1]: amount of assets to deposit into strategy i // toDeposit[strategies.length]: is there something to deposit } // Distribute matched shares and withdraw unamatched ones. for (uint256 i; i < strategies.length; ++i) { // Calculate amount of shares to distribute and amount of shares to redeem. uint256 sharesToRedeem; uint256 totalUnmatchedWithdrawals; { if (reallocationTable[i][i][0] == 0) { IStrategy(strategies[i]).beforeRedeemalCheck(0, reallocationParams.withdrawalSlippages[i]); // There is nothing to withdraw from strategy i. continue; } uint256[2] memory totals; // totals[0] -> total withdrawals // totals[1] -> total matched withdrawals for (uint256 j; j < strategies.length; ++j) { if (j == i) { // Strategy does not reallocate to itself. continue; } totals[0] += reallocationTable[i][j][0]; // Take smaller for matched withdrawals. if (reallocationTable[i][j][0] > reallocationTable[j][i][0]) { totals[1] += reallocationTable[j][i][0]; } else { totals[1] += reallocationTable[i][j][0]; } } // Unmatched withdrawals are difference between total and matched withdrawals. totalUnmatchedWithdrawals = totals[0] - totals[1]; // Calculate amount of shares to redeem and to distribute. uint256 sharesToDistribute = // first store here total amount of shares that should have been withdrawn reallocationTable[i][i][0]; IStrategy(strategies[i]).beforeRedeemalCheck( sharesToDistribute, reallocationParams.withdrawalSlippages[i] ); sharesToRedeem = sharesToDistribute * totalUnmatchedWithdrawals / totals[0]; sharesToDistribute -= sharesToRedeem; // Distribute matched shares to matched strategies. if (sharesToDistribute > 0) { for (uint256 j; j < strategies.length; ++j) { if (j == i) { // Strategy does not reallocate to itself. continue; } uint256 matched; // Take smaller for matched withdrawals. if (reallocationTable[i][j][0] > reallocationTable[j][i][0]) { matched = reallocationTable[j][i][0]; } else { matched = reallocationTable[i][j][0]; } if (matched == 0) { continue; } // Give shares to strategy j. reallocationTable[j][i][1] = sharesToDistribute * matched / totals[1]; sharesToDistribute -= reallocationTable[j][i][1]; // dust-less calculation totals[1] -= matched; } } } if (sharesToRedeem == 0) { // There is nothing to withdraw for strategy i. continue; } // Withdraw assets from underlying protocol. uint256[] memory withdrawnAssets = IStrategy(strategies[i]).redeemFast( sharesToRedeem, address(reallocationParams.masterWallet), assetGroup, reallocationParams.withdrawalSlippages[i] ); // Distribute withdrawn assets to strategies according to reallocation table. for (uint256 j; j < strategies.length; ++j) { if (reallocationTable[i][j][0] <= reallocationTable[j][i][0]) { // Diagonal values will be equal, no need to check for i == j. // Nothing to deposit into strategy j. continue; } for (uint256 k; k < assetGroup.length; ++k) { // Find out how much of asset k should go to strategy j. uint256 depositAmount = withdrawnAssets[k] * (reallocationTable[i][j][0] - reallocationTable[j][i][0]) / totalUnmatchedWithdrawals; toDeposit[j][k] += depositAmount; // Mark that there is something to deposit for strategy j. toDeposit[j][assetGroup.length] += 1; // Use this table to temporarily store value deposited from strategy i to strategy j. reallocationTable[i][j][2] += reallocationParams.priceFeedManager.assetToUsdCustomPrice( assetGroup[k], depositAmount, exchangeRates[k] ); withdrawnAssets[k] -= depositAmount; // dust-less calculation } totalUnmatchedWithdrawals -= (reallocationTable[i][j][0] - reallocationTable[j][i][0]); // dust-less calculation } } // Deposit assets into the underlying protocols. for (uint256 i; i < strategies.length; ++i) { IStrategy(strategies[i]).beforeDepositCheck(toDeposit[i], reallocationParams.depositSlippages[i]); if (toDeposit[i][assetGroup.length] == 0) { // There is nothing to deposit for this strategy. continue; } // Transfer assets from master wallet to the strategy for the deposit. for (uint256 j; j < assetGroup.length; ++j) { reallocationParams.masterWallet.transfer(IERC20(assetGroup[j]), strategies[i], toDeposit[i][j]); } // Do the deposit. uint256 mintedSsts = IStrategy(strategies[i]).depositFast( assetGroup, exchangeRates, reallocationParams.priceFeedManager, reallocationParams.depositSlippages[i], reallocationParams.swapInfo[i] ); // Figure total value of assets gathered to be deposited. uint256 totalDepositedValue = reallocationParams.priceFeedManager.assetToUsdCustomPriceBulk(assetGroup, toDeposit[i], exchangeRates); // Distribute the minted shares to strategies that deposited into this strategy. for (uint256 j; j < strategies.length; ++j) { if (reallocationTable[j][i][2] == 0) { // Diagonal element will be 0, no need to check i == j. // No shares to give to strategy j. continue; } // Calculate amount of shares to give to strategy j. uint256 shares = mintedSsts * reallocationTable[j][i][2] / totalDepositedValue; mintedSsts -= shares; // dust-less calculation totalDepositedValue -= reallocationTable[j][i][2]; // dust-less calculation // Overwrite this table with amount of given shares. reallocationTable[j][i][2] = shares; } } } /** * @dev Smart vaults claim strategy shares. * @param smartVaults Smart vaults involved in the reallocation. * @param strategies Set of strategies involved in the reallocation. * @param strategyMapping Mapping between smart vaults' strategies and set of all strategies. * @param reallocationTable Filled in reallocation table. * @param reallocations Realllocations needed by the smart vaults. */ function _claimShares( address[] calldata smartVaults, address[] calldata strategies, uint256[][] memory strategyMapping, uint256[][][] memory reallocationTable, uint256[][][] memory reallocations ) private { // Loop over smart vaults. for (uint256 i; i < smartVaults.length; ++i) { // Number of strategies for this smart vault. uint256 smartVaultStrategiesLength = strategyMapping[i].length; // Will store amount of shares to claim from each strategy, // plus two temporary variables used in the calculation. uint256[] memory toClaim = new uint256[](smartVaultStrategiesLength+2); // Find strategies that needed withdrawal. for (uint256 j; j < smartVaultStrategiesLength; ++j) { if (reallocations[i][0][j] == 0) { // Strategy didn't need any withdrawal. continue; } // Merging two uints into an array due to stack depth. uint256[] memory values = new uint256[](2); values[0] = reallocations[i][0][j]; // value to withdraw from strategy values[1] = reallocations[i][1][smartVaultStrategiesLength]; // total value to deposit // Find strategiest that needed deposit. for (uint256 k; k < smartVaultStrategiesLength; ++k) { if (reallocations[i][1][k] == 0) { // Strategy k had no deposits planned. continue; } // Find value that should have moved from strategy j to k. uint256 valueToDeposit = values[0] * reallocations[i][1][k] / values[1]; // Figure out amount strategy shares to claim: // - matched shares toClaim[smartVaultStrategiesLength] = reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][1] * valueToDeposit / reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][0]; // - unmatched toClaim[smartVaultStrategiesLength + 1] = reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][2] * valueToDeposit / reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][0]; reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][0] -= valueToDeposit; // dust-less calculation - reallocation level reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][1] -= toClaim[smartVaultStrategiesLength]; reallocationTable[strategyMapping[i][j]][strategyMapping[i][k]][2] -= toClaim[smartVaultStrategiesLength + 1]; values[0] -= valueToDeposit; // dust-less calculation - smart vault level values[1] -= reallocations[i][1][k]; // Total amount of strategy k shares to claim by this smart vault. toClaim[k] += toClaim[smartVaultStrategiesLength] + toClaim[smartVaultStrategiesLength + 1]; } } // Claim strategy shares. for (uint256 j; j < smartVaultStrategiesLength; ++j) { if (toClaim[j] == 0) { // No shares to claim for strategy j. continue; } IStrategy(strategies[strategyMapping[i][j]]).claimShares(smartVaults[i], toClaim[j]); } } } }
// 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 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 v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// 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 (last updated v4.7.0) (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155Upgradeable is IERC165Upgradeable { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// 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/ERC1155/extensions/IERC1155MetadataURI.sol) pragma solidity ^0.8.0; import "../IERC1155Upgradeable.sol"; /** * @dev Interface of the optional ERC1155MetadataExtension interface, as defined * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. * * _Available since v3.1._ */ interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable { /** * @dev Returns the URI for token type `id`. * * If the `\{id\}` substring is present in the URI, it must be replaced by * clients with the actual token type ID. */ function uri(uint256 id) external view returns (string memory); }
// 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; 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(); 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); }
// 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: BUSL-1.1 pragma solidity 0.8.17; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/utils/math/Math.sol"; import "../interfaces/IMasterWallet.sol"; import "../interfaces/IStrategy.sol"; import "../interfaces/IStrategyRegistry.sol"; import "../interfaces/IUsdPriceFeedManager.sol"; /** * @title Spool utility functions. * @notice This library gathers various utility functions. */ library SpoolUtils { /** * @notice Gets asset ratios for strategies as recorded at their last DHW. * Asset ratios are ordered according to each strategies asset group. * @param strategies_ Addresses of strategies. * @param strategyRegistry_ Strategy registry. * @return strategyRatios Required asset ratio for strategies. */ function getStrategyRatiosAtLastDhw(address[] calldata strategies_, IStrategyRegistry strategyRegistry_) public view returns (uint256[][] memory) { uint256[][] memory strategyRatios = new uint256[][](strategies_.length); for (uint256 i; i < strategies_.length; ++i) { strategyRatios[i] = strategyRegistry_.assetRatioAtLastDhw(strategies_[i]); } return strategyRatios; } /** * @notice Gets USD exchange rates for tokens. * The exchange rate is represented as a USD price for one token. * @param tokens_ Addresses of tokens. * @param priceFeedManager_ USD price feed mananger. * @return exchangeRates Exchange rates for tokens. */ function getExchangeRates(address[] calldata tokens_, IUsdPriceFeedManager priceFeedManager_) public view returns (uint256[] memory) { uint256[] memory exchangeRates = new uint256[](tokens_.length); for (uint256 i; i < tokens_.length; ++i) { exchangeRates[i] = priceFeedManager_.assetToUsd(tokens_[i], 10 ** priceFeedManager_.assetDecimals(tokens_[i])); } return exchangeRates; } /** * @dev Gets revert message when a low-level call reverts, so that it can * be bubbled-up to caller. * @param returnData_ Data returned from reverted low-level call. * @return revertMsg Original revert message if available, or default message otherwise. */ function getRevertMsg(bytes memory returnData_) public pure returns (string memory) { // if the _res length is less than 68, then the transaction failed silently (without a revert message) if (returnData_.length < 68) { return "SpoolUtils::_getRevertMsg: Transaction reverted silently."; } assembly { // slice the sig hash returnData_ := add(returnData_, 0x04) } return abi.decode(returnData_, (string)); // all that remains is the revert string } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts 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": [ "@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/=lib/openzeppelin-contracts/contracts/", "@solmate/=lib/solmate/src/", "create3/=lib/create3/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "sstore2/=lib/sstore2/contracts/", "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/", "lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/", "lib/openzeppelin-contracts-upgradeable:ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/", "lib/openzeppelin-contracts-upgradeable:forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/", "lib/solmate:ds-test/=lib/solmate/lib/ds-test/src/" ], "optimizer": { "enabled": true, "runs": 1111 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": { "src/libraries/ReallocationLib.sol": { "ReallocationLib": "0xec9452e6a13c44b80f1d2353c13ed9e3f8ebca64" }, "src/libraries/SpoolUtils.sol": { "SpoolUtils": "0x604ae7b696be794b7e5e40c2a62f109c3cdeceab" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ISpoolAccessControl","name":"accessControl_","type":"address"},{"internalType":"contract IAssetGroupRegistry","name":"assetGroupRegistry_","type":"address"},{"internalType":"contract IRiskManager","name":"riskManager_","type":"address"},{"internalType":"contract IDepositManager","name":"depositManager_","type":"address"},{"internalType":"contract IWithdrawalManager","name":"withdrawalManager_","type":"address"},{"internalType":"contract IStrategyRegistry","name":"strategyRegistry_","type":"address"},{"internalType":"contract IMasterWallet","name":"masterWallet_","type":"address"},{"internalType":"contract IUsdPriceFeedManager","name":"priceFeedManager_","type":"address"},{"internalType":"address","name":"ghostStrategy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ConfigurationAddressZero","type":"error"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"strategyIndex","type":"uint256"}],"name":"DhwNotRunYetForIndex","type":"error"},{"inputs":[],"name":"GhostVault","type":"error"},{"inputs":[],"name":"InvalidArrayLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"MissingRole","type":"error"},{"inputs":[],"name":"NotSameAssetGroup","type":"error"},{"inputs":[],"name":"NothingToFlush","type":"error"},{"inputs":[],"name":"NothingToSync","type":"error"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"OnlyViewExecution","type":"error"},{"inputs":[],"name":"ReallocationParametersExpired","type":"error"},{"inputs":[],"name":"SmartVaultAlreadyRegistered","type":"error"},{"inputs":[],"name":"SmartVaultNotRegisteredYet","type":"error"},{"inputs":[],"name":"StaticAllocationSmartVault","type":"error"},{"inputs":[],"name":"SystemPaused","type":"error"},{"inputs":[],"name":"VaultNotSynced","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"flushIndex","type":"uint256"}],"name":"SmartVaultFlushed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartVault","type":"address"},{"indexed":false,"internalType":"uint16a16","name":"newAllocations","type":"uint256"}],"name":"SmartVaultReallocated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartVault","type":"address"},{"components":[{"internalType":"uint256","name":"assetGroupId","type":"uint256"},{"internalType":"address[]","name":"strategies","type":"address[]"},{"internalType":"uint16a16","name":"strategyAllocation","type":"uint256"},{"internalType":"uint16","name":"managementFeePct","type":"uint16"},{"internalType":"uint16","name":"depositFeePct","type":"uint16"},{"internalType":"uint16","name":"performanceFeePct","type":"uint16"}],"indexed":false,"internalType":"struct SmartVaultRegistrationForm","name":"registrationForm","type":"tuple"}],"name":"SmartVaultRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"flushIndex","type":"uint256"}],"name":"SmartVaultSynced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"StrategyRemovedFromVault","type":"event"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"allocations","outputs":[{"internalType":"uint16a16","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"assetGroupId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"name":"claimSmartVaultTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"claimWithdrawal","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"referral","type":"address"},{"internalType":"bool","name":"doFlush","type":"bool"}],"internalType":"struct DepositBag","name":"bag","type":"tuple"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"depositRatio","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"flushIndex","type":"uint256"}],"name":"dhwIndexes","outputs":[{"internalType":"uint16a16","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"flushSmartVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"getLatestFlushIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"smartVaults","type":"address[]"},{"internalType":"address[]","name":"strategies","type":"address[]"},{"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[][]"},{"internalType":"uint256[][]","name":"depositSlippages","type":"uint256[][]"},{"internalType":"uint256[][]","name":"withdrawalSlippages","type":"uint256[][]"},{"internalType":"uint256[2][]","name":"exchangeRateSlippages","type":"uint256[2][]"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct ReallocateParamBag","name":"reallocateParams","type":"tuple"}],"name":"reallocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"recoverPendingDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"internalType":"struct RedeemBag","name":"bag","type":"tuple"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bool","name":"doFlush","type":"bool"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"internalType":"struct RedeemBag","name":"bag","type":"tuple"},{"internalType":"uint256[][]","name":"withdrawalSlippages","type":"uint256[][]"}],"name":"redeemFast","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"internalType":"struct RedeemBag","name":"bag","type":"tuple"},{"internalType":"uint256[][]","name":"withdrawalSlippages","type":"uint256[][]"},{"internalType":"address","name":"redeemer","type":"address"}],"name":"redeemFastView","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"internalType":"struct RedeemBag","name":"bag","type":"tuple"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"bool","name":"doFlush","type":"bool"}],"name":"redeemFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"components":[{"internalType":"uint256","name":"assetGroupId","type":"uint256"},{"internalType":"address[]","name":"strategies","type":"address[]"},{"internalType":"uint16a16","name":"strategyAllocation","type":"uint256"},{"internalType":"uint16","name":"managementFeePct","type":"uint16"},{"internalType":"uint16","name":"depositFeePct","type":"uint16"},{"internalType":"uint16","name":"performanceFeePct","type":"uint16"}],"internalType":"struct SmartVaultRegistrationForm","name":"registrationForm","type":"tuple"}],"name":"registerSmartVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address[]","name":"vaults","type":"address[]"},{"internalType":"bool","name":"disableStrategy","type":"bool"}],"name":"removeStrategyFromVaults","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"simulateSync","outputs":[{"internalType":"uint256","name":"oldTotalSVTs","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"simulateSyncWithBurn","outputs":[{"internalType":"uint256","name":"newBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"}],"name":"strategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"smartVault","type":"address"},{"internalType":"bool","name":"revertIfError","type":"bool"}],"name":"syncSmartVault","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101a06040523480156200001257600080fd5b506040516200644b3803806200644b8339810160408190526200003591620001fc565b886001600160a01b0381166200005e5760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0390811660805288166200008c5760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038716620000b45760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038616620000dc5760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038516620001045760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0384166200012c5760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038316620001545760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b0382166200017c5760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b038116620001a45760405163bb0e4c3560e01b815260040160405180910390fd5b6001600160a01b03978816610100529587166101205293861660a05291851660c052841660e052831661014052821661016052166101805250620002d3565b6001600160a01b0381168114620001f957600080fd5b50565b60008060008060008060008060006101208a8c0312156200021c57600080fd5b89516200022981620001e3565b60208b01519099506200023c81620001e3565b60408b01519098506200024f81620001e3565b60608b01519097506200026281620001e3565b60808b01519096506200027581620001e3565b60a08b01519095506200028881620001e3565b60c08b01519094506200029b81620001e3565b60e08b0151909350620002ae81620001e3565b6101008b0151909250620002c281620001e3565b809150509295985092959850929598565b60805160a05160c05160e0516101005161012051610140516101605161018051615fef6200045c60003960008181610ea2015281816126ce015281816128dc01526138290152600061256d01526000818161259c01526140c20152600081816122d0015261244a01526000818161047b0152818161077a015281816109a501528181610c53015281816111f1015281816118e201528181611ba901528181611ea10152818161215f0152818161253e01528181612de201528181613a700152613e860152600081816107eb01528181610ffb015281816113aa015281816117ff01526131fe015260008181611c9501528181612a210152818161302e0152818161332b0152613c3e01526000818161074001528181610af30152818161153c015281816119f201528181611e7701528181612ad401528181612f12015281816133ca0152818161373d01528181613b830152613f8d0152600081816127560152818161281101528181612c940152818161355501528181613d1b0152613d8a0152615fef6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c8063a1280e4d116100d8578063e1c2eea61161008c578063f8fdca7a11610066578063f8fdca7a1461036a578063fa15b91b1461037d578063fe19c1681461039057600080fd5b8063e1c2eea614610323578063e90173bd14610336578063f8a704861461034957600080fd5b8063ab1e74ca116100bd578063ab1e74ca146102cd578063bb9a7578146102e0578063c6a6d7cb1461030357600080fd5b8063a1280e4d14610288578063a1bd9cc1146102ba57600080fd5b806352a9039c1161012f5780636e6462d6116101145780636e6462d61461023957806373bf0e50146102625780637f03e6831461027557600080fd5b806352a9039c146101fd57806361189dd31461022657600080fd5b8063260c231311610160578063260c2313146101a4578063388bbfd3146101b757806339ebf823146101dd57600080fd5b80630d5745351461017c578063146c674314610191575b600080fd5b61018f61018a36600461455d565b6103a3565b005b61018f61019f36600461457a565b6104f3565b61018f6101b236600461455d565b6106d8565b6101ca6101c53660046145f7565b6108c0565b6040519081526020015b60405180910390f35b6101f06101eb36600461455d565b6108e9565b6040516101d4919061469f565b6101ca61020b36600461455d565b6001600160a01b031660009081526035602052604090205490565b6101ca6102343660046146fe565b61095f565b6101ca61024736600461455d565b6001600160a01b031660009081526032602052604090205490565b61018f610270366004614781565b610b87565b6101ca6102833660046145f7565b610ccb565b6101ca61029636600461455d565b6001600160a01b03166000908152603660205260409020546001600160801b031690565b61018f6102c83660046147af565b610d57565b6101ca6102db36600461492e565b61105e565b6102f36102ee36600461455d565b6115e4565b6040516101d494939291906149c0565b6103166103113660046149ef565b611aa7565b6040516101d49190614a60565b610316610331366004614a73565b611b07565b6101ca610344366004614adc565b611b2f565b61035c610357366004614b08565b611b5a565b6040516101d4929190614b9d565b61031661037836600461455d565b611e42565b6101ca61038b366004614bbf565b611fb3565b61018f61039e366004614bfa565b611fe8565b6103ab612754565b6103b361280f565b6103bc8161287c565b6001600160a01b03811660009081526033602090815260408083208054825181850281018501909352808352919290919083018282801561042657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610408575b50505050509050610436816128ce565b6001600160a01b03828116600090815260356020908152604080832054603290925291829020549151636e87f18d60e11b815260048101929092526104ef92859285917f0000000000000000000000000000000000000000000000000000000000000000169063dd0fe31a90602401600060405180830381865afa1580156104c2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104ea9190810190614c35565b612973565b5050565b6104fb612754565b6105257fadcdbe1f109205bda263b8a003b032ba3d5feba6ed7a93446d414c5591a9271033612c55565b6001600160a01b03821660009081526031602052604090205460ff1615610578576040517fdafba3c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0382166000908152603260209081526040909120823590556105a390820182614cc4565b6001600160a01b03841660009081526033602052604090206105c692909161442c565b5060405180606001604052808260600160208101906105e59190614d20565b61ffff1681526020016105fe60a0840160808501614d20565b61ffff16815260200161061760c0840160a08501614d20565b61ffff9081169091526001600160a01b038416600081815260346020908152604080832086518154888501519884015188166401000000000265ffff0000000019998916620100000263ffffffff19909216929098169190911717969096169490941790945560358452828120858401359055603190935291819020805460ff19166001179055517f3de86e7d916b4d19191e071d9ca9ba1c74cc94fc5fea1332b180acf42e27d536906106cc908490614d84565b60405180910390a25050565b6106e0612754565b6106e861280f565b6106f3600033612c55565b6106fc8161287c565b6001600160a01b0381811660009081526036602090815260408083205460338352818420603290935292819020549051636e87f18d60e11b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000084169363b261ea8b9386936001600160801b039091169290917f0000000000000000000000000000000000000000000000000000000000000000169063dd0fe31a90602401600060405180830381865afa1580156107c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107e99190810190614c35565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663471ab76c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610847573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086b9190614e3d565b6040518663ffffffff1660e01b815260040161088b959493929190614e98565b600060405180830381600087803b1580156108a557600080fd5b505af11580156108b9573d6000803e3d6000fd5b5050505050565b60006108ca612754565b6108d261280f565b6108df8484333386612d2e565b90505b9392505050565b6001600160a01b03811660009081526033602090815260409182902080548351818402810184019094528084526060939283018282801561095357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610935575b50505050509050919050565b6000610969612754565b61097161280f565b61097a8661287c565b6001600160a01b03868116600090815260326020526040808220549051636e87f18d60e11b815291927f0000000000000000000000000000000000000000000000000000000000000000169163dd0fe31a916109dc9160040190815260200190565b600060405180830381865afa1580156109f9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a219190810190614c35565b6001600160a01b0388166000908152603360209081526040918290208054835181840281018401909452808452939450610a9e938b9392830182828015610a9157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a73575b5050505050836000613106565b6001600160a01b03878116600090815260366020526040908190205490517f6521c194000000000000000000000000000000000000000000000000000000008152600160801b9091046001600160801b0316917f00000000000000000000000000000000000000000000000000000000000000001690636521c19490610b38908b908b908b908b908b908a90339081908c90600401614f3d565b6020604051808303816000875af1158015610b57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7b9190614fa9565b98975050505050505050565b610b8f612754565b610b9761280f565b610ba08261287c565b6104ef8260336000856001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c1d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bff575b505050506001600160a01b0386811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091527f0000000000000000000000000000000000000000000000000000000000000000909116915063dd0fe31a90602401600060405180830381865afa158015610c9d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cc59190810190614c35565b84613106565b6000610cd5612754565b610cdd61280f565b610d137f8175070940abf2fdc44d0ca002a58b0ee7538d3bf19eb0576843c4b91dd91b45610d0e602087018761455d565b612c55565b610d4a610d23602086018661455d565b7f3f4962f8c2b64c6192ef9b583127ce92f8061bd2cdb53f44f7d399d083bda8233361350e565b6108df8484853386612d2e565b610d5f61280f565b610d6a600033612c55565b60005b82811015610fbc576000848483818110610d8957610d89614fc2565b9050602002016020810190610d9e919061455d565b9050600060336000878786818110610db857610db8614fc2565b9050602002016020810190610dcd919061455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610e4157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610e23575b5050505050905060005b8151811015610fae57876001600160a01b0316828281518110610e7057610e70614fc2565b60200260200101516001600160a01b031603610fa6576001600160a01b038316600090815260336020526040902080547f0000000000000000000000000000000000000000000000000000000000000000919083908110610ed357610ed3614fc2565b6000918252602080832091909101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03948516179055918516815260359091526040812054610f229183906135f4565b6001600160a01b038416600090815260356020526040902055868685818110610f4d57610f4d614fc2565b9050602002016020810190610f62919061455d565b6001600160a01b0316886001600160a01b03167feb80b29f2addaaffe1908cea1e5251e60bf25cb5bef4c02f816e670ede404dfa60405160405180910390a3610fae565b600101610e4b565b505050806001019050610d6d565b508015611058576040517f175188e80000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063175188e890602401600060405180830381600087803b15801561103f57600080fd5b505af1158015611053573d6000803e3d6000fd5b505050505b50505050565b600061108b6040518060800160405280606081526020016060815260200160608152602001606081525090565b6040805180820182526000808252602082015290517fa7614d810000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063a7614d81906110e2908790600401614a60565b600060405180830381865afa1580156110ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111279190810190615024565b604080840191909152517f539183e70000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063539183e7906111759088908890600401615111565b600060405180830381865afa158015611192573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111ba919081019061518e565b60608301526001600160a01b0386811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000009091169063dd0fe31a90602401600060405180830381865afa15801561123a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112629190810190614c35565b82526001600160a01b038616600090815260336020908152604091829020805483518184028101840190945280845290918301828280156112cc57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ae575b50505050602080850192909252506001600160a01b03871660009081526036825260408082208151808301909252546001600160801b038082168352600160801b909104169281019290925290915061132c908790869085908581613640565b61133690846151d9565b925080600001516001600160801b031681602001516001600160801b0316036113605750506108e2565b6001600160a01b038087166000908152603760209081526040808320858301516001600160801b031684528252918290205490850151915163669d1b7160e11b81529092611431927f00000000000000000000000000000000000000000000000000000000000000009091169163cd3a36e2916113df9160040161469f565b600060405180830381865afa1580156113fc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611424919081019061518e565b828560200151600061381a565b61143d575050506108e2565b61144561449c565b506001600160a01b0387166000818152603860209081526040808320548151808301835287840180516001600160801b0390811683528286019390935286865260348552838620845160608082018752915461ffff80821683526201000082048116838a01526401000000009091041681870152855160e081018752988952888701849052958b01519488019490945289519387019390935260808601879052915191949293929160a08301916114fe918e9116613948565b81526020018390526040517f71b5cb8d0000000000000000000000000000000000000000000000000000000081529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906371b5cb8d9061157190859060040161520f565b600060405180830381865afa15801561158e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b691908101906152bf565b90506115cb8b8a8984600001518a6001613640565b6115d590896151d9565b9b9a5050505050505050505050565b6040805160608181018352600080835260208301819052928201839052829182919081906001600160a01b03871660008181526036602090815260408083208151808301835290546001600160801b038082168352600160801b909104168184015293835260338252808320805482518185028101850190935280835293949391929091908301828280156116a257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611684575b50505050509350886001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190614fa9565b6001600160a01b038a1660008181526034602090815260408083208151606081018352905461ffff808216835262010000820481168386015264010000000090910416818301529383526037825280832086830180516001600160801b0390811686529190935292205490518551949c5092965094509081169116036117e45787600080865167ffffffffffffffff8111156117a9576117a9614817565b6040519080825280602002602001820160405280156117d2578160200160208202803683370190505b50975097509750975050505050611aa0565b60405163669d1b7160e11b8152611882906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063cd3a36e29061183490889060040161469f565b600060405180830381865afa158015611851573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611879919081019061518e565b8386600061381a565b6118a45787600080865167ffffffffffffffff8111156117a9576117a9614817565b6118ac61449c565b6001600160a01b038a8116600090815260326020526040808220549051636e87f18d60e11b8152600481019190915290916060917f00000000000000000000000000000000000000000000000000000000000000009091169063dd0fe31a90602401600060405180830381865afa15801561192b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119539190810190614c35565b905061196c8c85602001516001600160801b0316613948565b6001600160a01b038d16600090815260386020908152604091829020548251808401909352878201516001600160801b0316835290820152935091506119b06144ba565b6040518060e001604052808e6001600160a01b0316815260200185815260200189815260200183815260200187815260200184815260200188815250905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166371b5cb8d836040518263ffffffff1660e01b8152600401611a3c919061520f565b600060405180830381865afa158015611a59573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8191908101906152bf565b8051604082015160a090920151909d50909b5099505050505050505050505b9193509193565b6060611ab1612754565b3215611af0576040517f2aa281970000000000000000000000000000000000000000000000000000000081523260048201526024015b60405180910390fd5b611afc85858585613991565b90505b949350505050565b6060611b11612754565b611b19613d19565b611b2584848433613991565b90506108e2613d88565b6001600160a01b03821660009081526037602090815260408083208484529091529020545b92915050565b60606000611b66612754565b611b6e61280f565b611b778861287c565b6001600160a01b03888116600090815260326020526040808220549051636e87f18d60e11b81526004810182905290927f0000000000000000000000000000000000000000000000000000000000000000169063dd0fe31a90602401600060405180830381865afa158015611bf0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611c189190810190614c35565b6001600160a01b038b166000908152603360209081526040918290208054835181840281018401909452808452939450611c93938e9392830182828015610a91576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610a73575050505050836000613106565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8b1a3e76040518061010001604052808d6001600160a01b031681526020018c8c808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506040805160208c810282810182019093528c82529283019290918d918d918291850190849080828437600081840152601f19601f820116905080830192505050505050508152602001886001600160a01b03168152602001336001600160a01b03168152602001858152602001848152602001603660008f6001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b03168152506040518263ffffffff1660e01b8152600401611dea9190615395565b6000604051808303816000875af1158015611e09573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e31919081019061545c565b935093505050965096945050505050565b6001600160a01b0381811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091526060917f000000000000000000000000000000000000000000000000000000000000000081169163ac18ea13917f0000000000000000000000000000000000000000000000000000000000000000169063dd0fe31a90602401600060405180830381865afa158015611ee8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f109190810190614c35565b6001600160a01b03851660009081526035602090815260408083205460339092529182902091517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b168152611f6e9392906004016154a3565b600060405180830381865afa158015611f8b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b54919081019061518e565b6000611fbd612754565b611fc561280f565b611fda611fd5602084018461455d565b61287c565b611b5482613de3565b919050565b611ff0612754565b611ff8613d19565b3215612028576120287f0a66f215ebc1d03d1ea20a9a71a7626b5d7c81bb86acae0bee93ba60b544a9f633612c55565b428160c001351015612066576040517fffea2f9600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120708180614cc4565b159050612749576120846040820182614cc4565b90506120936020830183614cc4565b90501415806120be57506120aa6060820182614cc4565b90506120b96020830183614cc4565b905014155b806120e557506120d16080820182614cc4565b90506120e06020830183614cc4565b905014155b1561210357604051634ec4810560e11b815260040160405180910390fd5b60006032816121128480614cc4565b600081811061212357612123614fc2565b9050602002016020810190612138919061455d565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dd0fe31a836040518263ffffffff1660e01b81526004016121ab91815260200190565b600060405180830381865afa1580156121c8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121f09190810190614c35565b905060005b6121ff8480614cc4565b905081101561252d5760006122148580614cc4565b8381811061222457612224614fc2565b9050602002016020810190612239919061455d565b90506122448161287c565b6001600160a01b0381166000908152603260205260409020548414612295576040517f061e1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f97939d3c0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906397939d3c90602401602060405180830381865afa158015612319573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233d9190614e3d565b6001600160a01b03160361237d576040517f337ce50200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6124078160336000846001600160a01b03166001600160a01b031681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156123fa57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116123dc575b5050505050856000613106565b6001600160a01b0381811660009081526033602052604080822090517fbb427f5000000000000000000000000000000000000000000000000000000000815291927f0000000000000000000000000000000000000000000000000000000000000000169163bb427f5091612480918691906004016154ce565b602060405180830381865afa15801561249d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c19190614fa9565b6001600160a01b0383166000818152603560205260409081902083905551919250907fee364d6312abc273abfa22ba799f321e049f2e38d0100fad80090594bc0fbff3906125129084815260200190565b60405180910390a2505080612526906154f0565b90506121f5565b5060006040518061010001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020018481526020018580604001906125df9190614cc4565b6125e891615509565b81526020016125fa6060870187614cc4565b6126039161568e565b81526020016126156080870187614cc4565b61261e9161568e565b815260200161263060a08701876156f6565b808060200260200160405190810160405280939291908181526020016000905b8282101561268e5760408051808201825290808402870190600290839083908082843760009201919091525050508152600190910190602001612650565b505050919092525090915073ec9452e6a13c44b80f1d2353c13ed9e3f8ebca64905063d24a280f6126bf8680614cc4565b6126cc6020890189614cc4565b7f000000000000000000000000000000000000000000000000000000000000000087603360356040518963ffffffff1660e01b815260040161271598979695949392919061594b565b60006040518083038186803b15801561272d57600080fd5b505af4158015612741573d6000803e3d6000fd5b505050505050505b612751613d88565b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d69190615a53565b1561280d576040517f729e4c4000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d8f1086a6040518163ffffffff1660e01b815260040160006040518083038186803b15801561286857600080fd5b505afa158015611058573d6000803e3d6000fd5b6001600160a01b03811660009081526031602052604090205460ff16612751576040517fcbf256f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8151811015612940577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682828151811061291657612916614fc2565b60200260200101516001600160a01b031614612930575050565b612939816154f0565b90506128d1565b506040517f08c6ead300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0384166000908152603660209081526040918290208251808401909352546001600160801b03808216808552600160801b90920416918301829052146129ec576040517f835fa20900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516040517f1fdd01a00000000000000000000000000000000000000000000000000000000081526000916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691631fdd01a091612a59918a91908990600401615a70565b6020604051808303816000875af1158015612a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9c9190614fa9565b82516040517ffa6c18d80000000000000000000000000000000000000000000000000000000081529192506000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163fa6c18d891612b0f918b918a908c908b90600401615aa1565b6020604051808303816000875af1158015612b2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b529190614fa9565b90508015612b5e578091505b81600003612b98576040517feb694a3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038716600081815260376020908152604080832087516001600160801b0390811685529083529281902086905586519051921682527fd785d6ac7a1beb66e7c7aa47d471a28dc52f21913125972a85b8cbd2bf6234de910160405180910390a2825183612c0b82615aea565b6001600160801b039081169091526001600160a01b039098166000908152603660209081526040909120855191909501518916600160801b02981697909717909255505050505050565b6040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301527f000000000000000000000000000000000000000000000000000000000000000016906391d1485490604401602060405180830381865afa158015612cdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cff9190615a53565b6104ef576040516301d4003760e61b8152600481018390526001600160a01b0382166024820152604401611ae7565b6000612d40611fd5602088018861455d565b6000603381612d5260208a018a61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015612dc657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612da8575b50505050509050612dd6816128ce565b60006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663dd0fe31a603283612e1760208d018d61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020546040518263ffffffff1660e01b8152600401612e5691815260200190565b600060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e9b9190810190614c35565b9050612eb6612ead60208a018a61455d565b83836000613106565b6000603681612ec860208c018c61455d565b6001600160a01b03908116825260208083019390935260409182016000208251808401909352546001600160801b038082168452600160801b9091041682840181905291935090917f000000000000000000000000000000000000000000000000000000000000000090911690636521c19490612f47908d018d61455d565b612f5460408e018e614cc4565b8e8060600190612f649190614cc4565b898f8f8a6040518a63ffffffff1660e01b8152600401612f8c99989796959493929190614f3d565b6020604051808303816000875af1158015612fab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fcf9190614fa9565b5050604080516080810182526001600160a01b038a8116825289811660208301528881168284015283516001600160801b0316606083015291517f3b72ca870000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000090921691633b72ca8791613066918d9190600401615b77565b6020604051808303816000875af1158015613085573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130a99190614fa9565b935084156130fa576130fa6130c160208b018b61455d565b603560006130d260208e018e61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020548585612973565b50505095945050505050565b6001600160a01b0384166000908152603660209081526040918290208251808401909352546001600160801b03808216808552600160801b909204169183018290520361318b578115613185576040517fcbf261db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50611058565b6001600160a01b038516600090815260376020908152604080832084830180516001600160801b03908116865291845282852054835180850190945280845290519094938301916131de918b9116613948565b905260405163669d1b7160e11b8152909150613280906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063cd3a36e290613233908a9060040161469f565b600060405180830381865afa158015613250573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613278919081019061518e565b83888761381a565b61328c57505050611058565b6001600160a01b0380881660008181526034602090815260408083208151606081018352905461ffff808216835262010000820481168386015264010000000090910416818301528151808301835289840180516001600160801b0316825295855260388452938290205492840192909252925192517fe0397fa0000000000000000000000000000000000000000000000000000000008152909391927f00000000000000000000000000000000000000000000000000000000000000009092169163e0397fa091613366918d918d908a90600401615bc7565b600060405180830381600087803b15801561338057600080fd5b505af1158015613394573d6000803e3d6000fd5b50506040517f41c4f075000000000000000000000000000000000000000000000000000000008152600092506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691506341c4f0759061340a908d9086908e908a908f908b90600401615c09565b6000604051808303816000875af1158015613429573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261345191908101906152bf565b6020808801516040516001600160801b0390911681529192506001600160a01b038c16917f206fbd6d84a86176a548e1fdfddab2bc0ec06020f85918c4b12f8f5716a3a81a910160405180910390a2602086018051906134b082615aea565b6001600160801b039081169091526001600160a01b038c1660009081526036602090815260408083208b519b8301518516600160801b029b9094169a909a179092559281015160389091529690912095909555505050505050505050565b6040517f12fa996c0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526024820184905282811660448301527f000000000000000000000000000000000000000000000000000000000000000016906312fa996c90606401602060405180830381865afa15801561359c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135c09190615a53565b6135ef576040516301d4003760e61b8152600481018390526001600160a01b0382166024820152604401611ae7565b505050565b60006010831061360357600080fd5b62010000821061361257600080fd5b61361d601084615ca6565b925081831b83613631600162010000615cbd565b901b1985161790509392505050565b6000805b865181101561380f577f800000000000000000000000000000000000000000000000000000000000000087828151811061368057613680614fc2565b6020026020010151116137ff57856060015181815181106136a3576136a3614fc2565b6020026020010151600003156137ff576000866040015182815181106136cb576136cb614fc2565b60200260200101518060200190518101906136e69190615cd0565b905083158015613707575084602001516001600160801b0316816040015110155b1561371257506137ff565b838015613730575084602001516001600160801b0316816040015114155b1561373b57506137ff565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663708215b08a838a60600151868151811061378257613782614fc2565b60200260200101518a8c600001516040518663ffffffff1660e01b81526004016137b0959493929190615d54565b602060405180830381865afa1580156137cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f19190614fa9565b6137fb90846151d9565b9250505b613808816154f0565b9050613644565b509695505050505050565b6000805b835181101561393c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684828151811061386357613863614fc2565b60200260200101516001600160a01b0316031561392c5785818151811061388c5761388c614fc2565b60200260200101516138a7828761417f90919063ffffffff16565b1061392c578215613922578381815181106138c4576138c4614fc2565b60200260200101516138df828761417f90919063ffffffff16565b6040517f751077a30000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401611ae7565b6000915050611aff565b613935816154f0565b905061381e565b50600195945050505050565b60008115613988576001600160a01b038316600090815260376020526040812090613974600185615cbd565b8152602001908152602001600020546108e2565b60009392505050565b60606139a3611fd5602087018761455d565b60006033816139b5602089018961455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015613a2957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a0b575b50505050509050600060326000886000016020810190613a49919061455d565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dd0fe31a836040518263ffffffff1660e01b8152600401613abc91815260200190565b600060405180830381865afa158015613ad9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b019190810190614c35565b83519091508614613b2557604051634ec4810560e11b815260040160405180910390fd5b613b3e613b3560208a018a61455d565b84836000613106565b6000603681613b5060208c018c61455d565b6001600160a01b039081168252602080830193909352604090910160002054600160801b90046001600160801b031692507f00000000000000000000000000000000000000000000000000000000000000001690636521c19490613bb6908c018c61455d565b613bc360408d018d614cc4565b613bd060608f018f614cc4565b888d8e8a6040518a63ffffffff1660e01b8152600401613bf899989796959493929190614f3d565b6020604051808303816000875af1158015613c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3b9190614fa9565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634fb62d478a6040518060a001604052808881526020018681526020018781526020018a6001600160a01b031681526020018c8c90613ca5919061568e565b8152506040518363ffffffff1660e01b8152600401613cc5929190615db4565b6000604051808303816000875af1158015613ce4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d0c919081019061518e565b9998505050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663488c20486040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613d7457600080fd5b505af1158015611058573d6000803e3d6000fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636fc9fc2e6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613d7457600080fd5b600080603381613df6602086018661455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015613e6a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613e4c575b50505050509050613e7a816128ce565b60006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663dd0fe31a603283613ebb602089018961455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020546040518263ffffffff1660e01b8152600401613efa91815260200190565b600060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613f3f9190810190614c35565b90506000603581613f53602088018861455d565b6001600160a01b03168152602080820192909252604001600020549150613f8990613f809087018761455d565b84846000613106565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663237d311d876040518060a00160405280336001600160a01b03168152602001878152602001888152602001868152602001603660008c6000016020810190613ffe919061455d565b6001600160a01b0316815260208101919091526040908101600020546001600160801b0316909152517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815261405e929190600401615e80565b6020604051808303816000875af115801561407d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140a19190614fa9565b905060005b6140b36020880188614cc4565b905081101561414857614138337f00000000000000000000000000000000000000000000000000000000000000006140ee60208b018b614cc4565b858181106140fe576140fe614fc2565b9050602002013587858151811061411757614117614fc2565b60200260200101516001600160a01b03166141b3909392919063ffffffff16565b614141816154f0565b90506140a6565b5061415960a0870160808801615f6d565b156141765761417661416e602088018861455d565b838686612973565b95945050505050565b60006010821061418e57600080fd5b61419c600162010000615cbd565b6141a7836010615ca6565b84901c16905092915050565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092018352602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65649084015261105892879291600091614279918516908490614309565b8051909150156135ef57808060200190518101906142979190615a53565b6135ef5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611ae7565b60606108df848460008585600080866001600160a01b031685876040516143309190615f8a565b60006040518083038185875af1925050503d806000811461436d576040519150601f19603f3d011682016040523d82523d6000602084013e614372565b606091505b50915091506143838783838761438e565b979650505050505050565b606083156143fd5782516000036143f6576001600160a01b0385163b6143f65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611ae7565b5081611aff565b611aff83838151156144125781518083602001fd5b8060405162461bcd60e51b8152600401611ae79190615fa6565b82805482825590600052602060002090810192821561448c579160200282015b8281111561448c57815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384351617825560209092019160019091019061444c565b50614498929150614533565b5090565b60405180604001604052806002906020820280368337509192915050565b6040518060e0016040528060006001600160a01b031681526020016144dd61449c565b81526020016060815260200160608152602001600081526020016000815260200161452e6040518060600160405280600061ffff168152602001600061ffff168152602001600061ffff1681525090565b905290565b5b808211156144985760008155600101614534565b6001600160a01b038116811461275157600080fd5b60006020828403121561456f57600080fd5b81356108e281614548565b6000806040838503121561458d57600080fd5b823561459881614548565b9150602083013567ffffffffffffffff8111156145b457600080fd5b830160c081860312156145c657600080fd5b809150509250929050565b6000608082840312156145e357600080fd5b50919050565b801515811461275157600080fd5b60008060006060848603121561460c57600080fd5b833567ffffffffffffffff81111561462357600080fd5b61462f868287016145d1565b935050602084013561464081614548565b91506040840135614650816145e9565b809150509250925092565b600081518084526020808501945080840160005b838110156146945781516001600160a01b03168752958201959082019060010161466f565b509495945050505050565b6020815260006108e2602083018461465b565b60008083601f8401126146c457600080fd5b50813567ffffffffffffffff8111156146dc57600080fd5b6020830191508360208260051b85010111156146f757600080fd5b9250929050565b60008060008060006060868803121561471657600080fd5b853561472181614548565b9450602086013567ffffffffffffffff8082111561473e57600080fd5b61474a89838a016146b2565b9096509450604088013591508082111561476357600080fd5b50614770888289016146b2565b969995985093965092949392505050565b6000806040838503121561479457600080fd5b823561479f81614548565b915060208301356145c6816145e9565b600080600080606085870312156147c557600080fd5b84356147d081614548565b9350602085013567ffffffffffffffff8111156147ec57600080fd5b6147f8878288016146b2565b909450925050604085013561480c816145e9565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff8111828210171561485057614850614817565b60405290565b6040516060810167ffffffffffffffff8111828210171561485057614850614817565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a2576148a2614817565b604052919050565b600067ffffffffffffffff8211156148c4576148c4614817565b5060051b60200190565b600082601f8301126148df57600080fd5b813560206148f46148ef836148aa565b614879565b82815260059290921b8401810191818101908684111561491357600080fd5b8286015b8481101561380f5780358352918301918301614917565b60008060006060848603121561494357600080fd5b833561494e81614548565b9250602084013561495e81614548565b9150604084013567ffffffffffffffff81111561497a57600080fd5b614986868287016148ce565b9150509250925092565b600081518084526020808501945080840160005b83811015614694578151875295820195908201906001016149a4565b8481528360208201528260408201526080606082015260006149e56080830184614990565b9695505050505050565b60008060008060608587031215614a0557600080fd5b843567ffffffffffffffff80821115614a1d57600080fd5b614a29888389016145d1565b95506020870135915080821115614a3f57600080fd5b50614a4c878288016146b2565b909450925050604085013561480c81614548565b6020815260006108e26020830184614990565b600080600060408486031215614a8857600080fd5b833567ffffffffffffffff80821115614aa057600080fd5b614aac878388016145d1565b94506020860135915080821115614ac257600080fd5b50614acf868287016146b2565b9497909650939450505050565b60008060408385031215614aef57600080fd5b8235614afa81614548565b946020939093013593505050565b60008060008060008060808789031215614b2157600080fd5b8635614b2c81614548565b9550602087013567ffffffffffffffff80821115614b4957600080fd5b614b558a838b016146b2565b90975095506040890135915080821115614b6e57600080fd5b50614b7b89828a016146b2565b9094509250506060870135614b8f81614548565b809150509295509295509295565b604081526000614bb06040830185614990565b90508260208301529392505050565b600060208284031215614bd157600080fd5b813567ffffffffffffffff811115614be857600080fd5b820160a081850312156108e257600080fd5b600060208284031215614c0c57600080fd5b813567ffffffffffffffff811115614c2357600080fd5b820160e081850312156108e257600080fd5b60006020808385031215614c4857600080fd5b825167ffffffffffffffff811115614c5f57600080fd5b8301601f81018513614c7057600080fd5b8051614c7e6148ef826148aa565b81815260059190911b82018301908381019087831115614c9d57600080fd5b928401925b82841015614383578351614cb581614548565b82529284019290840190614ca2565b6000808335601e19843603018112614cdb57600080fd5b83018035915067ffffffffffffffff821115614cf657600080fd5b6020019150600581901b36038213156146f757600080fd5b803561ffff81168114611fe357600080fd5b600060208284031215614d3257600080fd5b6108e282614d0e565b6000808335601e19843603018112614d5257600080fd5b830160208101925035905067ffffffffffffffff811115614d7257600080fd5b8060051b36038213156146f757600080fd5b6000602080835260e08301843582850152614da182860186614d3b565b60c06040870152918290529060009061010086015b81831015614de6578335614dc981614548565b6001600160a01b0316815292840192600192909201918401614db6565b60408801356060880152614dfc60608901614d0e565b61ffff811660808901529450614e1460808901614d0e565b61ffff811660a08901529450614e2c60a08901614d0e565b61ffff811660c08901529450614383565b600060208284031215614e4f57600080fd5b81516108e281614548565b6000815480845260208085019450836000528060002060005b838110156146945781546001600160a01b031687529582019560019182019101614e73565b60006001600160a01b0380881683526001600160801b038716602084015260a06040840152614eca60a0840187614e5a565b8381036060850152614edc818761465b565b9250508084166080840152509695505050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614f2457600080fd5b8260051b80836020870137939093016020019392505050565b60006001600160a01b03808c16835260e06020840152614f6160e084018b8d614ef2565b8381036040850152614f74818a8c614ef2565b90508381036060850152614f88818961465b565b9682166080850152509390931660a082015260c00152509695505050505050565b600060208284031215614fbb57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600067ffffffffffffffff821115614ff257614ff2614817565b50601f01601f191660200190565b60005b8381101561501b578181015183820152602001615003565b50506000910152565b6000602080838503121561503757600080fd5b825167ffffffffffffffff8082111561504f57600080fd5b818501915085601f83011261506357600080fd5b81516150716148ef826148aa565b81815260059190911b8301840190848101908883111561509057600080fd5b8585015b83811015615104578051858111156150ac5760008081fd5b8601603f81018b136150be5760008081fd5b8781015160406150d06148ef83614fd8565b8281528d828486010111156150e55760008081fd5b6150f4838c8301848701615000565b8652505050918601918601615094565b5098975050505050505050565b6001600160a01b03831681526040602082015260006108df6040830184614990565b600082601f83011261514457600080fd5b815160206151546148ef836148aa565b82815260059290921b8401810191818101908684111561517357600080fd5b8286015b8481101561380f5780518352918301918301615177565b6000602082840312156151a057600080fd5b815167ffffffffffffffff8111156151b757600080fd5b611aff84828501615133565b634e487b7160e01b600052601160045260246000fd5b80820180821115611b5457611b546151c3565b8060005b60028110156110585781518452602093840193909101906001016151f0565b602081526001600160a01b0382511660208201526000602083015161523760408401826151ec565b506040830151610140608084015261525361016084018261465b565b90506060840151601f198483030160a0850152615270828261465b565b915050608084015160c084015260a084015160e084015260c08401516152b7610100850182805161ffff908116835260208083015182169084015260409182015116910152565b509392505050565b6000602082840312156152d157600080fd5b815167ffffffffffffffff808211156152e957600080fd5b908301908185036101008112156152ff57600080fd5b61530761482d565b8351815260208401516020820152604084015160408201526060605f198301121561533157600080fd5b615339614856565b9150606084015182526080840151602083015260a0840151604083015281606082015260c0840151608082015260e084015191508282111561537a57600080fd5b61538687838601615133565b60a08201529695505050505050565b602081526153af6020820183516001600160a01b03169052565b600060208301516101008060408501526153cd610120850183614990565b91506040850151601f19808685030160608701526153eb8483614990565b93506060870151915061540960808701836001600160a01b03169052565b60808701516001600160a01b03811660a0880152915060a087015160c087015260c08701519150808685030160e087015250615445838261465b565b92505060e085015181850152508091505092915050565b6000806040838503121561546f57600080fd5b825167ffffffffffffffff81111561548657600080fd5b61549285828601615133565b925050602083015190509250929050565b6060815260006154b6606083018661465b565b84602084015282810360408401526149e58185614e5a565b6001600160a01b03831681526040602082015260006108df6040830184614e5a565b600060018201615502576155026151c3565b5060010190565b60006155176148ef846148aa565b80848252602080830192508560051b85013681111561553557600080fd5b855b8181101561568257803567ffffffffffffffff808211156155585760008081fd5b9088019036601f83011261556c5760008081fd5b813561557a6148ef826148aa565b81815260059190911b8301860190868101903683111561559a5760008081fd5b8785015b83811015615670578035858111156155b65760008081fd5b8601606036829003601f19018113156155cf5760008081fd5b6155d7614856565b8b8301356155e481614548565b81526040838101356155f581614548565b828e015291830135918883111561560c5760008081fd5b9282019236603f85011261562257600092508283fd5b8c84013592506156346148ef84614fd8565b83815236828587010111156156495760008081fd5b838286018f83013760009381018e019390935281019190915284525091880191880161559e565b50895250505094830194508201615537565b50919695505050505050565b600061569c6148ef846148aa565b80848252602080830192508560051b8501368111156156ba57600080fd5b855b8181101561568257803567ffffffffffffffff8111156156dc5760008081fd5b6156e836828a016148ce565b8652509382019382016156bc565b6000808335601e1984360301811261570d57600080fd5b83018035915067ffffffffffffffff82111561572857600080fd5b6020019150600681901b36038213156146f757600080fd5b8183526000602080850194508260005b8581101561469457813561576381614548565b6001600160a01b031687529582019590820190600101615750565b60008151808452615796816020860160208601615000565b601f01601f19169290920160200192915050565b600081518084526020808501808196506005915083821b81018387016000805b8781101561585f578484038b5282518051808652908801908886019080891b87018a01855b8281101561584957888203601f19018452845180516001600160a01b0390811684528d820151168d8401526040908101516060918401829052906158358185018361577e565b968e0196958e0195935050506001016157ef565b509d8a019d9650505092870192506001016157ca565b50919998505050505050505050565b600081518084526020808501808196508360051b810191508286016000805b868110156158dc578385038a52825180518087529087019087870190845b818110156158c7578351835292890192918901916001016158ab565b50509a87019a9550509185019160010161588d565b509298975050505050505050565b60008151808452602080850194508084016000805b8481101561593f57825188835b60028110156159295782518252918601919086019060010161590c565b50505060409790970196918301916001016158ff565b50959695505050505050565b60c08152600061595f60c083018a8c615740565b828103602084015261597281898b615740565b6001600160a01b03888116604086015284820360608601528751168152905061010060208701516159ae60208401826001600160a01b03169052565b5060408701516159c960408401826001600160a01b03169052565b506060870151606083015260808701518160808401526159eb828401826157aa565b91505060a087015182820360a0840152615a05828261586e565b91505060c087015182820360c0840152615a1f828261586e565b91505060e087015182820360e0840152615a3982826158ea565b60808601979097525050505060a001529695505050505050565b600060208284031215615a6557600080fd5b81516108e2816145e9565b6001600160a01b03841681526001600160801b0383166020820152606060408201526000611afc606083018461465b565b6001600160a01b03861681526001600160801b038516602082015260a060408201526000615ad260a083018661465b565b8460608401528281036080840152610b7b818561465b565b60006001600160801b03808316818103615b0657615b066151c3565b6001019392505050565b60008135615b1d81614548565b6001600160a01b0316835260208281013590840152615b3f6040830183614d3b565b60806040860152615b54608086018284614ef2565b915050615b646060840184614d3b565b85830360608701526149e5838284614ef2565b60a081526000615b8a60a0830185615b10565b90506001600160a01b0380845116602084015280602085015116604084015280604085015116606084015250606083015160808301529392505050565b6001600160a01b03851681526001600160801b0384166020820152608060408201526000615bf8608083018561465b565b905082606083015295945050505050565b60006101406001600160a01b03891683526020615c288185018a6151ec565b816060850152615c3a8285018961465b565b9150608084018760005b6002811015615c6157815183529183019190830190600101615c44565b5050505082810360c0840152615c77818661465b565b845161ffff90811660e08601526020860151811661010086015260408601511661012085015291506143839050565b8082028115828204841417611b5457611b546151c3565b81810381811115611b5457611b546151c3565b600060208284031215615ce257600080fd5b815167ffffffffffffffff80821115615cfa57600080fd5b9083019060608286031215615d0e57600080fd5b615d16614856565b825182811115615d2557600080fd5b615d3187828601615133565b825250602083015160208201526040830151604082015280935050505092915050565b6001600160a01b038616815260a0602082015260008551606060a0840152615d80610100840182614990565b9050602087015160c0840152604087015160e08401528560408401528460608401528281036080840152610b7b818561465b565b604081526000615dc76040830185615b10565b602083820381850152845160a08352615de360a084018261465b565b90508186015183820383850152615dfa828261465b565b915050604086015160408401526001600160a01b0360608701511660608401526080860151838203608085015281935080518083528383019450838160051b840101848301925060005b82811015615e7257601f19858303018752615e60828551614990565b96860196938601939150600101615e44565b509998505050505050505050565b6040815260008335615e9181614548565b6001600160a01b038082166040850152615eae6020870187614d3b565b925060a06060860152615ec560e086018483614ef2565b9250506040860135615ed681614548565b811660808501526060860135615eeb81614548565b811660a08501526080860135615f00816145e9565b80151560c086015250838203602085015280855116825250602084015160a06020830152615f3160a083018261465b565b905060408501518282036040840152615f4a828261465b565b915050606085015160608301526080850151608083015280925050509392505050565b600060208284031215615f7f57600080fd5b81356108e2816145e9565b60008251615f9c818460208701615000565b9190910192915050565b6020815260006108e2602083018461577e56fea264697066735822122090d6c1b3c6afc8504855b013e118df61fdf27b6e6f48b85edd053b2010a6640464736f6c634300081100330000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd130000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a000000000000000000000000c30018cbee9c8141f620e950cc9637c7a2ef2c9e000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15000000000000000000000000f5dcf1f6e4c661cb28c27fff88adde3522cfbe9100000000000000000000000038f1a78ad8956b45b48837657bd0884ba7ab485a000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b19411
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101775760003560e01c8063a1280e4d116100d8578063e1c2eea61161008c578063f8fdca7a11610066578063f8fdca7a1461036a578063fa15b91b1461037d578063fe19c1681461039057600080fd5b8063e1c2eea614610323578063e90173bd14610336578063f8a704861461034957600080fd5b8063ab1e74ca116100bd578063ab1e74ca146102cd578063bb9a7578146102e0578063c6a6d7cb1461030357600080fd5b8063a1280e4d14610288578063a1bd9cc1146102ba57600080fd5b806352a9039c1161012f5780636e6462d6116101145780636e6462d61461023957806373bf0e50146102625780637f03e6831461027557600080fd5b806352a9039c146101fd57806361189dd31461022657600080fd5b8063260c231311610160578063260c2313146101a4578063388bbfd3146101b757806339ebf823146101dd57600080fd5b80630d5745351461017c578063146c674314610191575b600080fd5b61018f61018a36600461455d565b6103a3565b005b61018f61019f36600461457a565b6104f3565b61018f6101b236600461455d565b6106d8565b6101ca6101c53660046145f7565b6108c0565b6040519081526020015b60405180910390f35b6101f06101eb36600461455d565b6108e9565b6040516101d4919061469f565b6101ca61020b36600461455d565b6001600160a01b031660009081526035602052604090205490565b6101ca6102343660046146fe565b61095f565b6101ca61024736600461455d565b6001600160a01b031660009081526032602052604090205490565b61018f610270366004614781565b610b87565b6101ca6102833660046145f7565b610ccb565b6101ca61029636600461455d565b6001600160a01b03166000908152603660205260409020546001600160801b031690565b61018f6102c83660046147af565b610d57565b6101ca6102db36600461492e565b61105e565b6102f36102ee36600461455d565b6115e4565b6040516101d494939291906149c0565b6103166103113660046149ef565b611aa7565b6040516101d49190614a60565b610316610331366004614a73565b611b07565b6101ca610344366004614adc565b611b2f565b61035c610357366004614b08565b611b5a565b6040516101d4929190614b9d565b61031661037836600461455d565b611e42565b6101ca61038b366004614bbf565b611fb3565b61018f61039e366004614bfa565b611fe8565b6103ab612754565b6103b361280f565b6103bc8161287c565b6001600160a01b03811660009081526033602090815260408083208054825181850281018501909352808352919290919083018282801561042657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610408575b50505050509050610436816128ce565b6001600160a01b03828116600090815260356020908152604080832054603290925291829020549151636e87f18d60e11b815260048101929092526104ef92859285917f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a169063dd0fe31a90602401600060405180830381865afa1580156104c2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104ea9190810190614c35565b612973565b5050565b6104fb612754565b6105257fadcdbe1f109205bda263b8a003b032ba3d5feba6ed7a93446d414c5591a9271033612c55565b6001600160a01b03821660009081526031602052604090205460ff1615610578576040517fdafba3c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0382166000908152603260209081526040909120823590556105a390820182614cc4565b6001600160a01b03841660009081526033602052604090206105c692909161442c565b5060405180606001604052808260600160208101906105e59190614d20565b61ffff1681526020016105fe60a0840160808501614d20565b61ffff16815260200161061760c0840160a08501614d20565b61ffff9081169091526001600160a01b038416600081815260346020908152604080832086518154888501519884015188166401000000000265ffff0000000019998916620100000263ffffffff19909216929098169190911717969096169490941790945560358452828120858401359055603190935291819020805460ff19166001179055517f3de86e7d916b4d19191e071d9ca9ba1c74cc94fc5fea1332b180acf42e27d536906106cc908490614d84565b60405180910390a25050565b6106e0612754565b6106e861280f565b6106f3600033612c55565b6106fc8161287c565b6001600160a01b0381811660009081526036602090815260408083205460338352818420603290935292819020549051636e87f18d60e11b815260048101919091527f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf84169363b261ea8b9386936001600160801b039091169290917f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a169063dd0fe31a90602401600060405180830381865afa1580156107c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107e99190810190614c35565b7f000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f156001600160a01b031663471ab76c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610847573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086b9190614e3d565b6040518663ffffffff1660e01b815260040161088b959493929190614e98565b600060405180830381600087803b1580156108a557600080fd5b505af11580156108b9573d6000803e3d6000fd5b5050505050565b60006108ca612754565b6108d261280f565b6108df8484333386612d2e565b90505b9392505050565b6001600160a01b03811660009081526033602090815260409182902080548351818402810184019094528084526060939283018282801561095357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610935575b50505050509050919050565b6000610969612754565b61097161280f565b61097a8661287c565b6001600160a01b03868116600090815260326020526040808220549051636e87f18d60e11b815291927f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a169163dd0fe31a916109dc9160040190815260200190565b600060405180830381865afa1580156109f9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a219190810190614c35565b6001600160a01b0388166000908152603360209081526040918290208054835181840281018401909452808452939450610a9e938b9392830182828015610a9157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a73575b5050505050836000613106565b6001600160a01b03878116600090815260366020526040908190205490517f6521c194000000000000000000000000000000000000000000000000000000008152600160801b9091046001600160801b0316917f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf1690636521c19490610b38908b908b908b908b908b908a90339081908c90600401614f3d565b6020604051808303816000875af1158015610b57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7b9190614fa9565b98975050505050505050565b610b8f612754565b610b9761280f565b610ba08261287c565b6104ef8260336000856001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c1d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bff575b505050506001600160a01b0386811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091527f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a909116915063dd0fe31a90602401600060405180830381865afa158015610c9d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cc59190810190614c35565b84613106565b6000610cd5612754565b610cdd61280f565b610d137f8175070940abf2fdc44d0ca002a58b0ee7538d3bf19eb0576843c4b91dd91b45610d0e602087018761455d565b612c55565b610d4a610d23602086018661455d565b7f3f4962f8c2b64c6192ef9b583127ce92f8061bd2cdb53f44f7d399d083bda8233361350e565b6108df8484853386612d2e565b610d5f61280f565b610d6a600033612c55565b60005b82811015610fbc576000848483818110610d8957610d89614fc2565b9050602002016020810190610d9e919061455d565b9050600060336000878786818110610db857610db8614fc2565b9050602002016020810190610dcd919061455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610e4157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610e23575b5050505050905060005b8151811015610fae57876001600160a01b0316828281518110610e7057610e70614fc2565b60200260200101516001600160a01b031603610fa6576001600160a01b038316600090815260336020526040902080547f000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b19411919083908110610ed357610ed3614fc2565b6000918252602080832091909101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03948516179055918516815260359091526040812054610f229183906135f4565b6001600160a01b038416600090815260356020526040902055868685818110610f4d57610f4d614fc2565b9050602002016020810190610f62919061455d565b6001600160a01b0316886001600160a01b03167feb80b29f2addaaffe1908cea1e5251e60bf25cb5bef4c02f816e670ede404dfa60405160405180910390a3610fae565b600101610e4b565b505050806001019050610d6d565b508015611058576040517f175188e80000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15169063175188e890602401600060405180830381600087803b15801561103f57600080fd5b505af1158015611053573d6000803e3d6000fd5b505050505b50505050565b600061108b6040518060800160405280606081526020016060815260200160608152602001606081525090565b6040805180820182526000808252602082015290517fa7614d810000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063a7614d81906110e2908790600401614a60565b600060405180830381865afa1580156110ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111279190810190615024565b604080840191909152517f539183e70000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063539183e7906111759088908890600401615111565b600060405180830381865afa158015611192573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111ba919081019061518e565b60608301526001600160a01b0386811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091527f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a9091169063dd0fe31a90602401600060405180830381865afa15801561123a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112629190810190614c35565b82526001600160a01b038616600090815260336020908152604091829020805483518184028101840190945280845290918301828280156112cc57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ae575b50505050602080850192909252506001600160a01b03871660009081526036825260408082208151808301909252546001600160801b038082168352600160801b909104169281019290925290915061132c908790869085908581613640565b61133690846151d9565b925080600001516001600160801b031681602001516001600160801b0316036113605750506108e2565b6001600160a01b038087166000908152603760209081526040808320858301516001600160801b031684528252918290205490850151915163669d1b7160e11b81529092611431927f000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f159091169163cd3a36e2916113df9160040161469f565b600060405180830381865afa1580156113fc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611424919081019061518e565b828560200151600061381a565b61143d575050506108e2565b61144561449c565b506001600160a01b0387166000818152603860209081526040808320548151808301835287840180516001600160801b0390811683528286019390935286865260348552838620845160608082018752915461ffff80821683526201000082048116838a01526401000000009091041681870152855160e081018752988952888701849052958b01519488019490945289519387019390935260808601879052915191949293929160a08301916114fe918e9116613948565b81526020018390526040517f71b5cb8d0000000000000000000000000000000000000000000000000000000081529091506000906001600160a01b037f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf16906371b5cb8d9061157190859060040161520f565b600060405180830381865afa15801561158e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b691908101906152bf565b90506115cb8b8a8984600001518a6001613640565b6115d590896151d9565b9b9a5050505050505050505050565b6040805160608181018352600080835260208301819052928201839052829182919081906001600160a01b03871660008181526036602090815260408083208151808301835290546001600160801b038082168352600160801b909104168184015293835260338252808320805482518185028101850190935280835293949391929091908301828280156116a257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611684575b50505050509350886001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170b9190614fa9565b6001600160a01b038a1660008181526034602090815260408083208151606081018352905461ffff808216835262010000820481168386015264010000000090910416818301529383526037825280832086830180516001600160801b0390811686529190935292205490518551949c5092965094509081169116036117e45787600080865167ffffffffffffffff8111156117a9576117a9614817565b6040519080825280602002602001820160405280156117d2578160200160208202803683370190505b50975097509750975050505050611aa0565b60405163669d1b7160e11b8152611882906001600160a01b037f000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15169063cd3a36e29061183490889060040161469f565b600060405180830381865afa158015611851573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611879919081019061518e565b8386600061381a565b6118a45787600080865167ffffffffffffffff8111156117a9576117a9614817565b6118ac61449c565b6001600160a01b038a8116600090815260326020526040808220549051636e87f18d60e11b8152600481019190915290916060917f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a9091169063dd0fe31a90602401600060405180830381865afa15801561192b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119539190810190614c35565b905061196c8c85602001516001600160801b0316613948565b6001600160a01b038d16600090815260386020908152604091829020548251808401909352878201516001600160801b0316835290820152935091506119b06144ba565b6040518060e001604052808e6001600160a01b0316815260200185815260200189815260200183815260200187815260200184815260200188815250905060007f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf6001600160a01b03166371b5cb8d836040518263ffffffff1660e01b8152600401611a3c919061520f565b600060405180830381865afa158015611a59573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8191908101906152bf565b8051604082015160a090920151909d50909b5099505050505050505050505b9193509193565b6060611ab1612754565b3215611af0576040517f2aa281970000000000000000000000000000000000000000000000000000000081523260048201526024015b60405180910390fd5b611afc85858585613991565b90505b949350505050565b6060611b11612754565b611b19613d19565b611b2584848433613991565b90506108e2613d88565b6001600160a01b03821660009081526037602090815260408083208484529091529020545b92915050565b60606000611b66612754565b611b6e61280f565b611b778861287c565b6001600160a01b03888116600090815260326020526040808220549051636e87f18d60e11b81526004810182905290927f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a169063dd0fe31a90602401600060405180830381865afa158015611bf0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611c189190810190614c35565b6001600160a01b038b166000908152603360209081526040918290208054835181840281018401909452808452939450611c93938e9392830182828015610a91576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610a73575050505050836000613106565b7f000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a6001600160a01b031663b8b1a3e76040518061010001604052808d6001600160a01b031681526020018c8c808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506040805160208c810282810182019093528c82529283019290918d918d918291850190849080828437600081840152601f19601f820116905080830192505050505050508152602001886001600160a01b03168152602001336001600160a01b03168152602001858152602001848152602001603660008f6001600160a01b03166001600160a01b0316815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b03168152506040518263ffffffff1660e01b8152600401611dea9190615395565b6000604051808303816000875af1158015611e09573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e31919081019061545c565b935093505050965096945050505050565b6001600160a01b0381811660009081526032602052604090819020549051636e87f18d60e11b815260048101919091526060917f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf81169163ac18ea13917f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a169063dd0fe31a90602401600060405180830381865afa158015611ee8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f109190810190614c35565b6001600160a01b03851660009081526035602090815260408083205460339092529182902091517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b168152611f6e9392906004016154a3565b600060405180830381865afa158015611f8b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b54919081019061518e565b6000611fbd612754565b611fc561280f565b611fda611fd5602084018461455d565b61287c565b611b5482613de3565b919050565b611ff0612754565b611ff8613d19565b3215612028576120287f0a66f215ebc1d03d1ea20a9a71a7626b5d7c81bb86acae0bee93ba60b544a9f633612c55565b428160c001351015612066576040517fffea2f9600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120708180614cc4565b159050612749576120846040820182614cc4565b90506120936020830183614cc4565b90501415806120be57506120aa6060820182614cc4565b90506120b96020830183614cc4565b905014155b806120e557506120d16080820182614cc4565b90506120e06020830183614cc4565b905014155b1561210357604051634ec4810560e11b815260040160405180910390fd5b60006032816121128480614cc4565b600081811061212357612123614fc2565b9050602002016020810190612138919061455d565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060007f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a6001600160a01b031663dd0fe31a836040518263ffffffff1660e01b81526004016121ab91815260200190565b600060405180830381865afa1580156121c8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121f09190810190614c35565b905060005b6121ff8480614cc4565b905081101561252d5760006122148580614cc4565b8381811061222457612224614fc2565b9050602002016020810190612239919061455d565b90506122448161287c565b6001600160a01b0381166000908152603260205260409020548414612295576040517f061e1ba600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f97939d3c0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301526000917f000000000000000000000000c30018cbee9c8141f620e950cc9637c7a2ef2c9e909116906397939d3c90602401602060405180830381865afa158015612319573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233d9190614e3d565b6001600160a01b03160361237d576040517f337ce50200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6124078160336000846001600160a01b03166001600160a01b031681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156123fa57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116123dc575b5050505050856000613106565b6001600160a01b0381811660009081526033602052604080822090517fbb427f5000000000000000000000000000000000000000000000000000000000815291927f000000000000000000000000c30018cbee9c8141f620e950cc9637c7a2ef2c9e169163bb427f5091612480918691906004016154ce565b602060405180830381865afa15801561249d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c19190614fa9565b6001600160a01b0383166000818152603560205260409081902083905551919250907fee364d6312abc273abfa22ba799f321e049f2e38d0100fad80090594bc0fbff3906125129084815260200190565b60405180910390a2505080612526906154f0565b90506121f5565b5060006040518061010001604052807f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a6001600160a01b031681526020017f00000000000000000000000038f1a78ad8956b45b48837657bd0884ba7ab485a6001600160a01b031681526020017f000000000000000000000000f5dcf1f6e4c661cb28c27fff88adde3522cfbe916001600160a01b031681526020018481526020018580604001906125df9190614cc4565b6125e891615509565b81526020016125fa6060870187614cc4565b6126039161568e565b81526020016126156080870187614cc4565b61261e9161568e565b815260200161263060a08701876156f6565b808060200260200160405190810160405280939291908181526020016000905b8282101561268e5760408051808201825290808402870190600290839083908082843760009201919091525050508152600190910190602001612650565b505050919092525090915073ec9452e6a13c44b80f1d2353c13ed9e3f8ebca64905063d24a280f6126bf8680614cc4565b6126cc6020890189614cc4565b7f000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b1941187603360356040518963ffffffff1660e01b815260040161271598979695949392919061594b565b60006040518083038186803b15801561272d57600080fd5b505af4158015612741573d6000803e3d6000fd5b505050505050505b612751613d88565b50565b7f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd136001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d69190615a53565b1561280d576040517f729e4c4000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd136001600160a01b031663d8f1086a6040518163ffffffff1660e01b815260040160006040518083038186803b15801561286857600080fd5b505afa158015611058573d6000803e3d6000fd5b6001600160a01b03811660009081526031602052604090205460ff16612751576040517fcbf256f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8151811015612940577f000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b194116001600160a01b031682828151811061291657612916614fc2565b60200260200101516001600160a01b031614612930575050565b612939816154f0565b90506128d1565b506040517f08c6ead300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0384166000908152603660209081526040918290208251808401909352546001600160801b03808216808552600160801b90920416918301829052146129ec576040517f835fa20900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516040517f1fdd01a00000000000000000000000000000000000000000000000000000000081526000916001600160a01b037f000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a1691631fdd01a091612a59918a91908990600401615a70565b6020604051808303816000875af1158015612a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9c9190614fa9565b82516040517ffa6c18d80000000000000000000000000000000000000000000000000000000081529192506000916001600160a01b037f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf169163fa6c18d891612b0f918b918a908c908b90600401615aa1565b6020604051808303816000875af1158015612b2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b529190614fa9565b90508015612b5e578091505b81600003612b98576040517feb694a3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038716600081815260376020908152604080832087516001600160801b0390811685529083529281902086905586519051921682527fd785d6ac7a1beb66e7c7aa47d471a28dc52f21913125972a85b8cbd2bf6234de910160405180910390a2825183612c0b82615aea565b6001600160801b039081169091526001600160a01b039098166000908152603660209081526040909120855191909501518916600160801b02981697909717909255505050505050565b6040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1316906391d1485490604401602060405180830381865afa158015612cdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cff9190615a53565b6104ef576040516301d4003760e61b8152600481018390526001600160a01b0382166024820152604401611ae7565b6000612d40611fd5602088018861455d565b6000603381612d5260208a018a61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015612dc657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612da8575b50505050509050612dd6816128ce565b60006001600160a01b037f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a1663dd0fe31a603283612e1760208d018d61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020546040518263ffffffff1660e01b8152600401612e5691815260200190565b600060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612e9b9190810190614c35565b9050612eb6612ead60208a018a61455d565b83836000613106565b6000603681612ec860208c018c61455d565b6001600160a01b03908116825260208083019390935260409182016000208251808401909352546001600160801b038082168452600160801b9091041682840181905291935090917f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf90911690636521c19490612f47908d018d61455d565b612f5460408e018e614cc4565b8e8060600190612f649190614cc4565b898f8f8a6040518a63ffffffff1660e01b8152600401612f8c99989796959493929190614f3d565b6020604051808303816000875af1158015612fab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fcf9190614fa9565b5050604080516080810182526001600160a01b038a8116825289811660208301528881168284015283516001600160801b0316606083015291517f3b72ca870000000000000000000000000000000000000000000000000000000081527f000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a90921691633b72ca8791613066918d9190600401615b77565b6020604051808303816000875af1158015613085573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130a99190614fa9565b935084156130fa576130fa6130c160208b018b61455d565b603560006130d260208e018e61455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020548585612973565b50505095945050505050565b6001600160a01b0384166000908152603660209081526040918290208251808401909352546001600160801b03808216808552600160801b909204169183018290520361318b578115613185576040517fcbf261db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50611058565b6001600160a01b038516600090815260376020908152604080832084830180516001600160801b03908116865291845282852054835180850190945280845290519094938301916131de918b9116613948565b905260405163669d1b7160e11b8152909150613280906001600160a01b037f000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15169063cd3a36e290613233908a9060040161469f565b600060405180830381865afa158015613250573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613278919081019061518e565b83888761381a565b61328c57505050611058565b6001600160a01b0380881660008181526034602090815260408083208151606081018352905461ffff808216835262010000820481168386015264010000000090910416818301528151808301835289840180516001600160801b0316825295855260388452938290205492840192909252925192517fe0397fa0000000000000000000000000000000000000000000000000000000008152909391927f000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a9092169163e0397fa091613366918d918d908a90600401615bc7565b600060405180830381600087803b15801561338057600080fd5b505af1158015613394573d6000803e3d6000fd5b50506040517f41c4f075000000000000000000000000000000000000000000000000000000008152600092506001600160a01b037f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf1691506341c4f0759061340a908d9086908e908a908f908b90600401615c09565b6000604051808303816000875af1158015613429573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261345191908101906152bf565b6020808801516040516001600160801b0390911681529192506001600160a01b038c16917f206fbd6d84a86176a548e1fdfddab2bc0ec06020f85918c4b12f8f5716a3a81a910160405180910390a2602086018051906134b082615aea565b6001600160801b039081169091526001600160a01b038c1660009081526036602090815260408083208b519b8301518516600160801b029b9094169a909a179092559281015160389091529690912095909555505050505050505050565b6040517f12fa996c0000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526024820184905282811660448301527f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd1316906312fa996c90606401602060405180830381865afa15801561359c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135c09190615a53565b6135ef576040516301d4003760e61b8152600481018390526001600160a01b0382166024820152604401611ae7565b505050565b60006010831061360357600080fd5b62010000821061361257600080fd5b61361d601084615ca6565b925081831b83613631600162010000615cbd565b901b1985161790509392505050565b6000805b865181101561380f577f800000000000000000000000000000000000000000000000000000000000000087828151811061368057613680614fc2565b6020026020010151116137ff57856060015181815181106136a3576136a3614fc2565b6020026020010151600003156137ff576000866040015182815181106136cb576136cb614fc2565b60200260200101518060200190518101906136e69190615cd0565b905083158015613707575084602001516001600160801b0316816040015110155b1561371257506137ff565b838015613730575084602001516001600160801b0316816040015114155b1561373b57506137ff565b7f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf6001600160a01b031663708215b08a838a60600151868151811061378257613782614fc2565b60200260200101518a8c600001516040518663ffffffff1660e01b81526004016137b0959493929190615d54565b602060405180830381865afa1580156137cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f19190614fa9565b6137fb90846151d9565b9250505b613808816154f0565b9050613644565b509695505050505050565b6000805b835181101561393c577f000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b194116001600160a01b031684828151811061386357613863614fc2565b60200260200101516001600160a01b0316031561392c5785818151811061388c5761388c614fc2565b60200260200101516138a7828761417f90919063ffffffff16565b1061392c578215613922578381815181106138c4576138c4614fc2565b60200260200101516138df828761417f90919063ffffffff16565b6040517f751077a30000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401611ae7565b6000915050611aff565b613935816154f0565b905061381e565b50600195945050505050565b60008115613988576001600160a01b038316600090815260376020526040812090613974600185615cbd565b8152602001908152602001600020546108e2565b60009392505050565b60606139a3611fd5602087018761455d565b60006033816139b5602089018961455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015613a2957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a0b575b50505050509050600060326000886000016020810190613a49919061455d565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060007f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a6001600160a01b031663dd0fe31a836040518263ffffffff1660e01b8152600401613abc91815260200190565b600060405180830381865afa158015613ad9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b019190810190614c35565b83519091508614613b2557604051634ec4810560e11b815260040160405180910390fd5b613b3e613b3560208a018a61455d565b84836000613106565b6000603681613b5060208c018c61455d565b6001600160a01b039081168252602080830193909352604090910160002054600160801b90046001600160801b031692507f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf1690636521c19490613bb6908c018c61455d565b613bc360408d018d614cc4565b613bd060608f018f614cc4565b888d8e8a6040518a63ffffffff1660e01b8152600401613bf899989796959493929190614f3d565b6020604051808303816000875af1158015613c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3b9190614fa9565b507f000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a6001600160a01b0316634fb62d478a6040518060a001604052808881526020018681526020018781526020018a6001600160a01b031681526020018c8c90613ca5919061568e565b8152506040518363ffffffff1660e01b8152600401613cc5929190615db4565b6000604051808303816000875af1158015613ce4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d0c919081019061518e565b9998505050505050505050565b7f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd136001600160a01b031663488c20486040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613d7457600080fd5b505af1158015611058573d6000803e3d6000fd5b7f0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd136001600160a01b0316636fc9fc2e6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015613d7457600080fd5b600080603381613df6602086018661455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015613e6a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613e4c575b50505050509050613e7a816128ce565b60006001600160a01b037f0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a1663dd0fe31a603283613ebb602089018961455d565b6001600160a01b03166001600160a01b03168152602001908152602001600020546040518263ffffffff1660e01b8152600401613efa91815260200190565b600060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613f3f9190810190614c35565b90506000603581613f53602088018861455d565b6001600160a01b03168152602080820192909252604001600020549150613f8990613f809087018761455d565b84846000613106565b60007f000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf6001600160a01b031663237d311d876040518060a00160405280336001600160a01b03168152602001878152602001888152602001868152602001603660008c6000016020810190613ffe919061455d565b6001600160a01b0316815260208101919091526040908101600020546001600160801b0316909152517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815261405e929190600401615e80565b6020604051808303816000875af115801561407d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140a19190614fa9565b905060005b6140b36020880188614cc4565b905081101561414857614138337f000000000000000000000000f5dcf1f6e4c661cb28c27fff88adde3522cfbe916140ee60208b018b614cc4565b858181106140fe576140fe614fc2565b9050602002013587858151811061411757614117614fc2565b60200260200101516001600160a01b03166141b3909392919063ffffffff16565b614141816154f0565b90506140a6565b5061415960a0870160808801615f6d565b156141765761417661416e602088018861455d565b838686612973565b95945050505050565b60006010821061418e57600080fd5b61419c600162010000615cbd565b6141a7836010615ca6565b84901c16905092915050565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092018352602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65649084015261105892879291600091614279918516908490614309565b8051909150156135ef57808060200190518101906142979190615a53565b6135ef5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611ae7565b60606108df848460008585600080866001600160a01b031685876040516143309190615f8a565b60006040518083038185875af1925050503d806000811461436d576040519150601f19603f3d011682016040523d82523d6000602084013e614372565b606091505b50915091506143838783838761438e565b979650505050505050565b606083156143fd5782516000036143f6576001600160a01b0385163b6143f65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611ae7565b5081611aff565b611aff83838151156144125781518083602001fd5b8060405162461bcd60e51b8152600401611ae79190615fa6565b82805482825590600052602060002090810192821561448c579160200282015b8281111561448c57815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384351617825560209092019160019091019061444c565b50614498929150614533565b5090565b60405180604001604052806002906020820280368337509192915050565b6040518060e0016040528060006001600160a01b031681526020016144dd61449c565b81526020016060815260200160608152602001600081526020016000815260200161452e6040518060600160405280600061ffff168152602001600061ffff168152602001600061ffff1681525090565b905290565b5b808211156144985760008155600101614534565b6001600160a01b038116811461275157600080fd5b60006020828403121561456f57600080fd5b81356108e281614548565b6000806040838503121561458d57600080fd5b823561459881614548565b9150602083013567ffffffffffffffff8111156145b457600080fd5b830160c081860312156145c657600080fd5b809150509250929050565b6000608082840312156145e357600080fd5b50919050565b801515811461275157600080fd5b60008060006060848603121561460c57600080fd5b833567ffffffffffffffff81111561462357600080fd5b61462f868287016145d1565b935050602084013561464081614548565b91506040840135614650816145e9565b809150509250925092565b600081518084526020808501945080840160005b838110156146945781516001600160a01b03168752958201959082019060010161466f565b509495945050505050565b6020815260006108e2602083018461465b565b60008083601f8401126146c457600080fd5b50813567ffffffffffffffff8111156146dc57600080fd5b6020830191508360208260051b85010111156146f757600080fd5b9250929050565b60008060008060006060868803121561471657600080fd5b853561472181614548565b9450602086013567ffffffffffffffff8082111561473e57600080fd5b61474a89838a016146b2565b9096509450604088013591508082111561476357600080fd5b50614770888289016146b2565b969995985093965092949392505050565b6000806040838503121561479457600080fd5b823561479f81614548565b915060208301356145c6816145e9565b600080600080606085870312156147c557600080fd5b84356147d081614548565b9350602085013567ffffffffffffffff8111156147ec57600080fd5b6147f8878288016146b2565b909450925050604085013561480c816145e9565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff8111828210171561485057614850614817565b60405290565b6040516060810167ffffffffffffffff8111828210171561485057614850614817565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a2576148a2614817565b604052919050565b600067ffffffffffffffff8211156148c4576148c4614817565b5060051b60200190565b600082601f8301126148df57600080fd5b813560206148f46148ef836148aa565b614879565b82815260059290921b8401810191818101908684111561491357600080fd5b8286015b8481101561380f5780358352918301918301614917565b60008060006060848603121561494357600080fd5b833561494e81614548565b9250602084013561495e81614548565b9150604084013567ffffffffffffffff81111561497a57600080fd5b614986868287016148ce565b9150509250925092565b600081518084526020808501945080840160005b83811015614694578151875295820195908201906001016149a4565b8481528360208201528260408201526080606082015260006149e56080830184614990565b9695505050505050565b60008060008060608587031215614a0557600080fd5b843567ffffffffffffffff80821115614a1d57600080fd5b614a29888389016145d1565b95506020870135915080821115614a3f57600080fd5b50614a4c878288016146b2565b909450925050604085013561480c81614548565b6020815260006108e26020830184614990565b600080600060408486031215614a8857600080fd5b833567ffffffffffffffff80821115614aa057600080fd5b614aac878388016145d1565b94506020860135915080821115614ac257600080fd5b50614acf868287016146b2565b9497909650939450505050565b60008060408385031215614aef57600080fd5b8235614afa81614548565b946020939093013593505050565b60008060008060008060808789031215614b2157600080fd5b8635614b2c81614548565b9550602087013567ffffffffffffffff80821115614b4957600080fd5b614b558a838b016146b2565b90975095506040890135915080821115614b6e57600080fd5b50614b7b89828a016146b2565b9094509250506060870135614b8f81614548565b809150509295509295509295565b604081526000614bb06040830185614990565b90508260208301529392505050565b600060208284031215614bd157600080fd5b813567ffffffffffffffff811115614be857600080fd5b820160a081850312156108e257600080fd5b600060208284031215614c0c57600080fd5b813567ffffffffffffffff811115614c2357600080fd5b820160e081850312156108e257600080fd5b60006020808385031215614c4857600080fd5b825167ffffffffffffffff811115614c5f57600080fd5b8301601f81018513614c7057600080fd5b8051614c7e6148ef826148aa565b81815260059190911b82018301908381019087831115614c9d57600080fd5b928401925b82841015614383578351614cb581614548565b82529284019290840190614ca2565b6000808335601e19843603018112614cdb57600080fd5b83018035915067ffffffffffffffff821115614cf657600080fd5b6020019150600581901b36038213156146f757600080fd5b803561ffff81168114611fe357600080fd5b600060208284031215614d3257600080fd5b6108e282614d0e565b6000808335601e19843603018112614d5257600080fd5b830160208101925035905067ffffffffffffffff811115614d7257600080fd5b8060051b36038213156146f757600080fd5b6000602080835260e08301843582850152614da182860186614d3b565b60c06040870152918290529060009061010086015b81831015614de6578335614dc981614548565b6001600160a01b0316815292840192600192909201918401614db6565b60408801356060880152614dfc60608901614d0e565b61ffff811660808901529450614e1460808901614d0e565b61ffff811660a08901529450614e2c60a08901614d0e565b61ffff811660c08901529450614383565b600060208284031215614e4f57600080fd5b81516108e281614548565b6000815480845260208085019450836000528060002060005b838110156146945781546001600160a01b031687529582019560019182019101614e73565b60006001600160a01b0380881683526001600160801b038716602084015260a06040840152614eca60a0840187614e5a565b8381036060850152614edc818761465b565b9250508084166080840152509695505050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614f2457600080fd5b8260051b80836020870137939093016020019392505050565b60006001600160a01b03808c16835260e06020840152614f6160e084018b8d614ef2565b8381036040850152614f74818a8c614ef2565b90508381036060850152614f88818961465b565b9682166080850152509390931660a082015260c00152509695505050505050565b600060208284031215614fbb57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600067ffffffffffffffff821115614ff257614ff2614817565b50601f01601f191660200190565b60005b8381101561501b578181015183820152602001615003565b50506000910152565b6000602080838503121561503757600080fd5b825167ffffffffffffffff8082111561504f57600080fd5b818501915085601f83011261506357600080fd5b81516150716148ef826148aa565b81815260059190911b8301840190848101908883111561509057600080fd5b8585015b83811015615104578051858111156150ac5760008081fd5b8601603f81018b136150be5760008081fd5b8781015160406150d06148ef83614fd8565b8281528d828486010111156150e55760008081fd5b6150f4838c8301848701615000565b8652505050918601918601615094565b5098975050505050505050565b6001600160a01b03831681526040602082015260006108df6040830184614990565b600082601f83011261514457600080fd5b815160206151546148ef836148aa565b82815260059290921b8401810191818101908684111561517357600080fd5b8286015b8481101561380f5780518352918301918301615177565b6000602082840312156151a057600080fd5b815167ffffffffffffffff8111156151b757600080fd5b611aff84828501615133565b634e487b7160e01b600052601160045260246000fd5b80820180821115611b5457611b546151c3565b8060005b60028110156110585781518452602093840193909101906001016151f0565b602081526001600160a01b0382511660208201526000602083015161523760408401826151ec565b506040830151610140608084015261525361016084018261465b565b90506060840151601f198483030160a0850152615270828261465b565b915050608084015160c084015260a084015160e084015260c08401516152b7610100850182805161ffff908116835260208083015182169084015260409182015116910152565b509392505050565b6000602082840312156152d157600080fd5b815167ffffffffffffffff808211156152e957600080fd5b908301908185036101008112156152ff57600080fd5b61530761482d565b8351815260208401516020820152604084015160408201526060605f198301121561533157600080fd5b615339614856565b9150606084015182526080840151602083015260a0840151604083015281606082015260c0840151608082015260e084015191508282111561537a57600080fd5b61538687838601615133565b60a08201529695505050505050565b602081526153af6020820183516001600160a01b03169052565b600060208301516101008060408501526153cd610120850183614990565b91506040850151601f19808685030160608701526153eb8483614990565b93506060870151915061540960808701836001600160a01b03169052565b60808701516001600160a01b03811660a0880152915060a087015160c087015260c08701519150808685030160e087015250615445838261465b565b92505060e085015181850152508091505092915050565b6000806040838503121561546f57600080fd5b825167ffffffffffffffff81111561548657600080fd5b61549285828601615133565b925050602083015190509250929050565b6060815260006154b6606083018661465b565b84602084015282810360408401526149e58185614e5a565b6001600160a01b03831681526040602082015260006108df6040830184614e5a565b600060018201615502576155026151c3565b5060010190565b60006155176148ef846148aa565b80848252602080830192508560051b85013681111561553557600080fd5b855b8181101561568257803567ffffffffffffffff808211156155585760008081fd5b9088019036601f83011261556c5760008081fd5b813561557a6148ef826148aa565b81815260059190911b8301860190868101903683111561559a5760008081fd5b8785015b83811015615670578035858111156155b65760008081fd5b8601606036829003601f19018113156155cf5760008081fd5b6155d7614856565b8b8301356155e481614548565b81526040838101356155f581614548565b828e015291830135918883111561560c5760008081fd5b9282019236603f85011261562257600092508283fd5b8c84013592506156346148ef84614fd8565b83815236828587010111156156495760008081fd5b838286018f83013760009381018e019390935281019190915284525091880191880161559e565b50895250505094830194508201615537565b50919695505050505050565b600061569c6148ef846148aa565b80848252602080830192508560051b8501368111156156ba57600080fd5b855b8181101561568257803567ffffffffffffffff8111156156dc5760008081fd5b6156e836828a016148ce565b8652509382019382016156bc565b6000808335601e1984360301811261570d57600080fd5b83018035915067ffffffffffffffff82111561572857600080fd5b6020019150600681901b36038213156146f757600080fd5b8183526000602080850194508260005b8581101561469457813561576381614548565b6001600160a01b031687529582019590820190600101615750565b60008151808452615796816020860160208601615000565b601f01601f19169290920160200192915050565b600081518084526020808501808196506005915083821b81018387016000805b8781101561585f578484038b5282518051808652908801908886019080891b87018a01855b8281101561584957888203601f19018452845180516001600160a01b0390811684528d820151168d8401526040908101516060918401829052906158358185018361577e565b968e0196958e0195935050506001016157ef565b509d8a019d9650505092870192506001016157ca565b50919998505050505050505050565b600081518084526020808501808196508360051b810191508286016000805b868110156158dc578385038a52825180518087529087019087870190845b818110156158c7578351835292890192918901916001016158ab565b50509a87019a9550509185019160010161588d565b509298975050505050505050565b60008151808452602080850194508084016000805b8481101561593f57825188835b60028110156159295782518252918601919086019060010161590c565b50505060409790970196918301916001016158ff565b50959695505050505050565b60c08152600061595f60c083018a8c615740565b828103602084015261597281898b615740565b6001600160a01b03888116604086015284820360608601528751168152905061010060208701516159ae60208401826001600160a01b03169052565b5060408701516159c960408401826001600160a01b03169052565b506060870151606083015260808701518160808401526159eb828401826157aa565b91505060a087015182820360a0840152615a05828261586e565b91505060c087015182820360c0840152615a1f828261586e565b91505060e087015182820360e0840152615a3982826158ea565b60808601979097525050505060a001529695505050505050565b600060208284031215615a6557600080fd5b81516108e2816145e9565b6001600160a01b03841681526001600160801b0383166020820152606060408201526000611afc606083018461465b565b6001600160a01b03861681526001600160801b038516602082015260a060408201526000615ad260a083018661465b565b8460608401528281036080840152610b7b818561465b565b60006001600160801b03808316818103615b0657615b066151c3565b6001019392505050565b60008135615b1d81614548565b6001600160a01b0316835260208281013590840152615b3f6040830183614d3b565b60806040860152615b54608086018284614ef2565b915050615b646060840184614d3b565b85830360608701526149e5838284614ef2565b60a081526000615b8a60a0830185615b10565b90506001600160a01b0380845116602084015280602085015116604084015280604085015116606084015250606083015160808301529392505050565b6001600160a01b03851681526001600160801b0384166020820152608060408201526000615bf8608083018561465b565b905082606083015295945050505050565b60006101406001600160a01b03891683526020615c288185018a6151ec565b816060850152615c3a8285018961465b565b9150608084018760005b6002811015615c6157815183529183019190830190600101615c44565b5050505082810360c0840152615c77818661465b565b845161ffff90811660e08601526020860151811661010086015260408601511661012085015291506143839050565b8082028115828204841417611b5457611b546151c3565b81810381811115611b5457611b546151c3565b600060208284031215615ce257600080fd5b815167ffffffffffffffff80821115615cfa57600080fd5b9083019060608286031215615d0e57600080fd5b615d16614856565b825182811115615d2557600080fd5b615d3187828601615133565b825250602083015160208201526040830151604082015280935050505092915050565b6001600160a01b038616815260a0602082015260008551606060a0840152615d80610100840182614990565b9050602087015160c0840152604087015160e08401528560408401528460608401528281036080840152610b7b818561465b565b604081526000615dc76040830185615b10565b602083820381850152845160a08352615de360a084018261465b565b90508186015183820383850152615dfa828261465b565b915050604086015160408401526001600160a01b0360608701511660608401526080860151838203608085015281935080518083528383019450838160051b840101848301925060005b82811015615e7257601f19858303018752615e60828551614990565b96860196938601939150600101615e44565b509998505050505050505050565b6040815260008335615e9181614548565b6001600160a01b038082166040850152615eae6020870187614d3b565b925060a06060860152615ec560e086018483614ef2565b9250506040860135615ed681614548565b811660808501526060860135615eeb81614548565b811660a08501526080860135615f00816145e9565b80151560c086015250838203602085015280855116825250602084015160a06020830152615f3160a083018261465b565b905060408501518282036040840152615f4a828261465b565b915050606085015160608301526080850151608083015280925050509392505050565b600060208284031215615f7f57600080fd5b81356108e2816145e9565b60008251615f9c818460208701615000565b9190910192915050565b6020815260006108e2602083018461577e56fea264697066735822122090d6c1b3c6afc8504855b013e118df61fdf27b6e6f48b85edd053b2010a6640464736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd130000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a000000000000000000000000c30018cbee9c8141f620e950cc9637c7a2ef2c9e000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15000000000000000000000000f5dcf1f6e4c661cb28c27fff88adde3522cfbe9100000000000000000000000038f1a78ad8956b45b48837657bd0884ba7ab485a000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b19411
-----Decoded View---------------
Arg [0] : accessControl_ (address): 0x7b533e72E0cDC63AacD8cDB926AC402b846Fbd13
Arg [1] : assetGroupRegistry_ (address): 0x1Aa2a802BA25669531Ffd2b1fF8ae94f3D87f41A
Arg [2] : riskManager_ (address): 0xC30018cbee9C8141F620e950CC9637c7A2eF2c9e
Arg [3] : depositManager_ (address): 0x823Ba38992825FF37E72B6c3D669a09173B8F7bf
Arg [4] : withdrawalManager_ (address): 0xC62AD2C374D6537E286a06F4d6301DA6cd9C6D0a
Arg [5] : strategyRegistry_ (address): 0x554c6bCB54656390aca0a0af38CA954dbE653F15
Arg [6] : masterWallet_ (address): 0xf5DcF1F6E4c661Cb28c27FfF88Adde3522Cfbe91
Arg [7] : priceFeedManager_ (address): 0x38f1A78ad8956B45b48837657BD0884BA7AB485A
Arg [8] : ghostStrategy (address): 0xc65c1df280AC96535e56B0e7ac12fE0860b19411
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000007b533e72e0cdc63aacd8cdb926ac402b846fbd13
Arg [1] : 0000000000000000000000001aa2a802ba25669531ffd2b1ff8ae94f3d87f41a
Arg [2] : 000000000000000000000000c30018cbee9c8141f620e950cc9637c7a2ef2c9e
Arg [3] : 000000000000000000000000823ba38992825ff37e72b6c3d669a09173b8f7bf
Arg [4] : 000000000000000000000000c62ad2c374d6537e286a06f4d6301da6cd9c6d0a
Arg [5] : 000000000000000000000000554c6bcb54656390aca0a0af38ca954dbe653f15
Arg [6] : 000000000000000000000000f5dcf1f6e4c661cb28c27fff88adde3522cfbe91
Arg [7] : 00000000000000000000000038f1a78ad8956b45b48837657bd0884ba7ab485a
Arg [8] : 000000000000000000000000c65c1df280ac96535e56b0e7ac12fe0860b19411
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.