ETH Price: $2,235.40 (-2.52%)

Contract

0x5C5293EabBC96a62788b4CA0D393042D4e931e81
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

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
File 1 of 37 : SmartVaultManager.sol
// 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();
    }
}

File 2 of 37 : ERC20.sol
// 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 {}
}

File 3 of 37 : SafeERC20.sol
// 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");
        }
    }
}

File 4 of 37 : Math.sol
// 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);
        }
    }
}

File 5 of 37 : IAction.sol
// 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);
}

File 6 of 37 : IAssetGroupRegistry.sol
// 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);
}

File 7 of 37 : IDepositManager.sol
// 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);
}

File 8 of 37 : IGuardManager.sol
// 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);
}

File 9 of 37 : IMasterWallet.sol
// 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;
}

File 10 of 37 : IRiskManager.sol
// 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);
}

File 11 of 37 : ISmartVault.sol
// 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);
}

File 12 of 37 : ISmartVaultManager.sol
// 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);
}

File 13 of 37 : IStrategy.sol
// 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);
}

File 14 of 37 : IStrategyRegistry.sol
// 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;
}

File 15 of 37 : IUsdPriceFeedManager.sol
// 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);
}

File 16 of 37 : IWithdrawalManager.sol
// 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);
}

File 17 of 37 : CommonErrors.sol
// 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_);

File 18 of 37 : Constants.sol
// 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;

File 19 of 37 : RequestType.sol
// 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
}

File 20 of 37 : SpoolAccessControllable.sol
// 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;
}

File 21 of 37 : ArrayMapping.sol
// 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];
        }
    }
}

File 22 of 37 : uint16a16Lib.sol
// 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;
    }
}

File 23 of 37 : ReallocationLib.sol
// 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]);
            }
        }
    }
}

File 24 of 37 : IERC20.sol
// 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);
}

File 25 of 37 : IERC20Metadata.sol
// 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);
}

File 26 of 37 : Context.sol
// 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;
    }
}

File 27 of 37 : IERC20Permit.sol
// 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);
}

File 28 of 37 : Address.sol
// 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);
        }
    }
}

File 29 of 37 : IERC1155Upgradeable.sol
// 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;
}

File 30 of 37 : IERC20Upgradeable.sol
// 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);
}

File 31 of 37 : IERC1155MetadataURIUpgradeable.sol
// 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);
}

File 32 of 37 : ISwapper.sol
// 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);
}

File 33 of 37 : ISpoolAccessControl.sol
// 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);
}

File 34 of 37 : Roles.sol
// 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");

File 35 of 37 : SpoolUtils.sol
// 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
    }
}

File 36 of 37 : IERC165Upgradeable.sol
// 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);
}

File 37 of 37 : IAccessControlUpgradeable.sol
// 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;
}

Settings
{
  "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

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"}]

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.