ETH Price: $3,487.10 (+2.13%)
Gas: 2 Gwei

Contract

0x91A6194F1278f6Cf25ae51B604029075695A74E5
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Partial Withdraw199937772024-06-01 1:46:3549 days ago1717206395IN
0x91A6194F...5695A74E5
0 ETH0.000590945.61922876
Partial Withdraw198287052024-05-08 23:46:5972 days ago1715212019IN
0x91A6194F...5695A74E5
0 ETH0.000414943.94533459
Withdraw198251292024-05-08 11:45:2372 days ago1715168723IN
0x91A6194F...5695A74E5
0 ETH0.000412534.35020093
Partial Withdraw198251172024-05-08 11:42:5972 days ago1715168579IN
0x91A6194F...5695A74E5
0 ETH0.0004664.43161347
Withdraw198251052024-05-08 11:40:3572 days ago1715168435IN
0x91A6194F...5695A74E5
0 ETH0.00051074.69233986
Partial Withdraw194999702024-03-23 21:28:59118 days ago1711229339IN
0x91A6194F...5695A74E5
0 ETH0.0148643815.20467635
Partial Withdraw194937912024-03-23 0:40:23119 days ago1711154423IN
0x91A6194F...5695A74E5
0 ETH0.0055208215.47485487
Claim Yield192515142024-02-18 1:44:23153 days ago1708220663IN
0x91A6194F...5695A74E5
0 ETH0.0067789914.38041376
Partial Withdraw188475702023-12-23 9:18:23209 days ago1703323103IN
0x91A6194F...5695A74E5
0 ETH0.0065493618.63548947
Partial Withdraw187571742023-12-10 17:02:59222 days ago1702227779IN
0x91A6194F...5695A74E5
0 ETH0.0116683733.18867793
Partial Withdraw186695742023-11-28 10:37:47234 days ago1701167867IN
0x91A6194F...5695A74E5
0 ETH0.0035068127.94189087
Claim Yield186480022023-11-25 10:05:47237 days ago1700906747IN
0x91A6194F...5695A74E5
0 ETH0.0035339726.88354204
Partial Withdraw185919252023-11-17 13:34:35245 days ago1700228075IN
0x91A6194F...5695A74E5
0 ETH0.0103674224
Claim Yield185919182023-11-17 13:33:11245 days ago1700227991IN
0x91A6194F...5695A74E5
0 ETH0.0032863725
Partial Withdraw185833252023-11-16 8:42:35246 days ago1700124155IN
0x91A6194F...5695A74E5
0 ETH0.0031210724.86837005
Withdraw185799752023-11-15 21:29:35247 days ago1700083775IN
0x91A6194F...5695A74E5
0 ETH0.0178319940.56284287
Partial Withdraw185796802023-11-15 20:30:23247 days ago1700080223IN
0x91A6194F...5695A74E5
0 ETH0.0084328461.48721389
Claim Yield184064342023-10-22 14:28:59271 days ago1697984939IN
0x91A6194F...5695A74E5
0 ETH0.00089357.81338846
Partial Withdraw184064292023-10-22 14:27:59271 days ago1697984879IN
0x91A6194F...5695A74E5
0 ETH0.001019038.11953818
Partial Withdraw184063762023-10-22 14:17:11271 days ago1697984231IN
0x91A6194F...5695A74E5
0 ETH0.001176589.37484357
Partial Withdraw182345972023-09-28 13:42:35295 days ago1695908555IN
0x91A6194F...5695A74E5
0 ETH0.0017940514.29347237
Partial Withdraw180995642023-09-09 14:55:59314 days ago1694271359IN
0x91A6194F...5695A74E5
0 ETH0.0020788716.56419801
Partial Withdraw180720512023-09-05 18:26:35318 days ago1693938395IN
0x91A6194F...5695A74E5
0 ETH0.003945721.05092637
Partial Withdraw180720442023-09-05 18:25:11318 days ago1693938311IN
0x91A6194F...5695A74E5
0 ETH0.0038097922.93233175
Claim Yield180711222023-09-05 15:18:23318 days ago1693927103IN
0x91A6194F...5695A74E5
0 ETH0.0026830323.46233787
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 24 : Vault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";

import {IVault} from "./vault/IVault.sol";
import {IVaultSponsoring} from "./vault/IVaultSponsoring.sol";
import {IVaultSettings} from "./vault/IVaultSettings.sol";
import {CurveSwapper} from "./vault/CurveSwapper.sol";
import {PercentMath} from "./lib/PercentMath.sol";
import {ExitPausable} from "./lib/ExitPausable.sol";
import {IStrategy} from "./strategy/IStrategy.sol";
import {CustomErrors} from "./interfaces/CustomErrors.sol";

/**
 * A vault where other accounts can deposit an underlying token
 * currency and set distribution params for their principal and yield
 *
 * @notice The underlying token can be automatically swapped from any configured ERC20 token via {CurveSwapper}
 */
contract Vault is
    IVault,
    IVaultSponsoring,
    IVaultSettings,
    CurveSwapper,
    Context,
    ERC165,
    AccessControl,
    ReentrancyGuard,
    Pausable,
    ExitPausable,
    CustomErrors
{
    using SafeERC20 for IERC20;
    using SafeERC20 for IERC20Metadata;
    using PercentMath for uint256;
    using PercentMath for uint16;
    using Counters for Counters.Counter;

    //
    // Constants
    //

    /// Role allowed to invest/desinvest from strategy
    bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");

    /// Role allowed to change settings such as performance fee and investment fee
    bytes32 public constant SETTINGS_ROLE = keccak256("SETTINGS_ROLE");

    /// Role for sponsors allowed to call sponsor/unsponsor
    bytes32 public constant SPONSOR_ROLE = keccak256("SPONSOR_ROLE");

    /// Minimum lock for each sponsor
    uint64 public constant MIN_SPONSOR_LOCK_DURATION = 2 weeks;

    /// Maximum lock for each sponsor
    uint64 public constant MAX_SPONSOR_LOCK_DURATION = 24 weeks;

    /// Maximum lock for each deposit
    uint64 public constant MAX_DEPOSIT_LOCK_DURATION = 24 weeks;

    /// Helper constant for computing shares without losing precision
    uint256 public constant SHARES_MULTIPLIER = 1e18;

    //
    // State
    //

    /// @inheritdoc IVault
    IERC20Metadata public override(IVault) underlying;

    /// @inheritdoc IVault
    uint16 public override(IVault) investPct;

    /// @inheritdoc IVault
    uint64 public immutable override(IVault) minLockPeriod;

    /// @inheritdoc IVaultSponsoring
    uint256 public override(IVaultSponsoring) totalSponsored;

    /// @inheritdoc IVault
    uint256 public override(IVault) totalShares;

    /// The investment strategy
    IStrategy public strategy;

    /// Unique IDs to correlate donations that belong to the same foundation
    uint256 private _depositGroupIds;
    mapping(uint256 => address) public depositGroupIdOwner;

    /// deposit ID => deposit data
    mapping(uint256 => Deposit) public deposits;

    /// Counter for deposit ids
    Counters.Counter private _depositTokenIds;

    /// claimer address => claimer data
    mapping(address => Claimer) public claimer;

    /// The total of principal deposited
    uint256 public totalPrincipal;

    /// Treasury address to collect performance fee
    address public treasury;

    /// Performance fee percentage
    uint16 public perfFeePct;

    /// Current accumulated performance fee;
    uint256 public accumulatedPerfFee;

    /// Loss tolerance pct
    uint16 public lossTolerancePct;

    /// Rebalance minimum
    uint256 private immutable rebalanceMinimum;

    /**
     * @param _underlying Underlying ERC20 token to use.
     * @param _minLockPeriod Minimum lock period to deposit
     * @param _investPct Percentage of the total underlying to invest in the strategy
     * @param _treasury Treasury address to collect performance fee
     * @param _admin Vault admin address
     * @param _perfFeePct Performance fee percentage
     * @param _lossTolerancePct Loss tolerance when investing through the strategy
     * @param _swapPools Swap pools used to automatically convert tokens to underlying
     */
    constructor(
        IERC20Metadata _underlying,
        uint64 _minLockPeriod,
        uint16 _investPct,
        address _treasury,
        address _admin,
        uint16 _perfFeePct,
        uint16 _lossTolerancePct,
        SwapPoolParam[] memory _swapPools
    ) {
        if (!_investPct.validPct()) revert VaultInvalidInvestpct();
        if (!_perfFeePct.validPct()) revert VaultInvalidPerformanceFee();
        if (!_lossTolerancePct.validPct()) revert VaultInvalidLossTolerance();
        if (address(_underlying) == address(0x0))
            revert VaultUnderlyingCannotBe0Address();
        if (_treasury == address(0x0)) revert VaultTreasuryCannotBe0Address();
        if (_admin == address(0x0)) revert VaultAdminCannotBe0Address();
        if (_minLockPeriod == 0 || _minLockPeriod > MAX_DEPOSIT_LOCK_DURATION)
            revert VaultInvalidMinLockPeriod();

        _grantRole(DEFAULT_ADMIN_ROLE, _admin);
        _grantRole(KEEPER_ROLE, _admin);
        _grantRole(SETTINGS_ROLE, _admin);
        _grantRole(SPONSOR_ROLE, _admin);

        investPct = _investPct;
        underlying = _underlying;
        treasury = _treasury;
        minLockPeriod = _minLockPeriod;
        perfFeePct = _perfFeePct;
        lossTolerancePct = _lossTolerancePct;

        rebalanceMinimum = 10 * 10**underlying.decimals();

        _addPools(_swapPools);

        emit TreasuryUpdated(_treasury);
    }

    //
    // Modifiers
    //

    modifier onlyAdmin() {
        if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender))
            revert VaultCallerNotAdmin();
        _;
    }

    modifier onlySettings() {
        if (!hasRole(SETTINGS_ROLE, msg.sender))
            revert VaultCallerNotSettings();
        _;
    }

    modifier onlyKeeper() {
        if (!hasRole(KEEPER_ROLE, msg.sender)) revert VaultCallerNotKeeper();
        _;
    }

    modifier onlySponsor() {
        if (!hasRole(SPONSOR_ROLE, msg.sender)) revert VaultCallerNotSponsor();
        _;
    }

    /**
     * Transfers administrator rights for the Vault to another account,
     * revoking all current admin's roles and setting up the roles for the new admin.
     *
     * @notice Can only be called by the admin.
     *
     * @param _newAdmin The new admin account.
     */
    function transferAdminRights(address _newAdmin) external onlyAdmin {
        if (_newAdmin == address(0x0)) revert VaultAdminCannotBe0Address();
        if (_newAdmin == msg.sender)
            revert VaultCannotTransferAdminRightsToSelf();

        _grantRole(DEFAULT_ADMIN_ROLE, _newAdmin);
        _grantRole(KEEPER_ROLE, _newAdmin);
        _grantRole(SETTINGS_ROLE, _newAdmin);
        _grantRole(SPONSOR_ROLE, _newAdmin);

        _revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _revokeRole(KEEPER_ROLE, msg.sender);
        _revokeRole(SETTINGS_ROLE, msg.sender);
        _revokeRole(SPONSOR_ROLE, msg.sender);
    }

    //
    // IVault
    //

    /// @inheritdoc IVault
    function totalUnderlying() public view override(IVault) returns (uint256) {
        if (address(strategy) != address(0)) {
            return
                underlying.balanceOf(address(this)) + strategy.investedAssets();
        }

        return underlying.balanceOf(address(this));
    }

    /// @inheritdoc IVault
    function yieldFor(address _to)
        public
        view
        override(IVault)
        returns (
            uint256 claimableYield,
            uint256 shares,
            uint256 perfFee
        )
    {
        uint256 claimerPrincipal = claimer[_to].totalPrincipal;
        uint256 claimerShares = claimer[_to].totalShares;
        uint256 _totalUnderlyingMinusSponsored = totalUnderlyingMinusSponsored();

        uint256 currentClaimerPrincipal = _computeAmount(
            claimerShares,
            totalShares,
            _totalUnderlyingMinusSponsored
        );

        if (currentClaimerPrincipal <= claimerPrincipal) {
            return (0, 0, 0);
        }

        uint256 yieldWithPerfFee = currentClaimerPrincipal - claimerPrincipal;

        shares = _computeShares(
            yieldWithPerfFee,
            totalShares,
            _totalUnderlyingMinusSponsored
        );
        uint256 sharesAmount = _computeAmount(
            shares,
            totalShares,
            _totalUnderlyingMinusSponsored
        );

        perfFee = sharesAmount.pctOf(perfFeePct);
        claimableYield = sharesAmount - perfFee;
    }

    /// @inheritdoc IVault
    function depositForGroupId(uint256 _groupId, DepositParams calldata _params)
        external
        nonReentrant
        whenNotPaused
        returns (uint256[] memory depositIds)
    {
        if (depositGroupIdOwner[_groupId] != msg.sender)
            revert VaultSenderNotOwnerOfGroupId();

        depositIds = _doDeposit(_groupId, _params);
    }

    /// @inheritdoc IVault
    function deposit(DepositParams calldata _params)
        external
        nonReentrant
        whenNotPaused
        returns (uint256[] memory depositIds)
    {
        uint256 depositGroupId = _depositGroupIds;
        _depositGroupIds = depositGroupId + 1;

        depositGroupIdOwner[depositGroupId] = msg.sender;
        depositIds = _doDeposit(depositGroupId, _params);
    }

    function _doDeposit(uint256 _groupId, DepositParams calldata _params)
        internal
        returns (uint256[] memory depositIds)
    {
        if (_params.amount == 0) revert VaultCannotDeposit0();
        if (
            _params.lockDuration < minLockPeriod ||
            _params.lockDuration > MAX_DEPOSIT_LOCK_DURATION
        ) revert VaultInvalidLockPeriod();
        if (bytes(_params.name).length < 3) revert VaultDepositNameTooShort();

        uint256 principalMinusStrategyFee = _applyLossTolerance(totalPrincipal);
        uint256 previousTotalUnderlying = totalUnderlyingMinusSponsored();
        if (principalMinusStrategyFee > previousTotalUnderlying)
            revert VaultCannotDepositWhenYieldNegative();

        _transferAndCheckInputToken(
            msg.sender,
            _params.inputToken,
            _params.amount
        );
        uint256 newUnderlyingAmount = _swapIntoUnderlying(
            _params.inputToken,
            _params.amount,
            _params.slippage
        );

        uint64 lockedUntil = _params.lockDuration + _blockTimestamp();

        depositIds = _createDeposit(
            previousTotalUnderlying,
            newUnderlyingAmount,
            lockedUntil,
            _params.claims,
            _params.name,
            _groupId
        );
    }

    /// @inheritdoc IVault
    function claimYield(address _to)
        external
        override(IVault)
        nonReentrant
        whenNotExitPaused
    {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        (uint256 yield, uint256 shares, uint256 fee) = yieldFor(msg.sender);

        if (yield == 0) revert VaultNoYieldToClaim();

        uint256 _totalUnderlyingMinusSponsored = totalUnderlyingMinusSponsored();
        uint256 _totalShares = totalShares;

        accumulatedPerfFee += fee;

        _rebalanceBeforeWithdrawing(yield);

        underlying.safeTransfer(_to, yield);

        claimer[msg.sender].totalShares -= shares;
        totalShares -= shares;

        emit YieldClaimed(
            msg.sender,
            _to,
            yield,
            shares,
            fee,
            _totalUnderlyingMinusSponsored,
            _totalShares
        );
    }

    /// @inheritdoc IVault
    function withdraw(address _to, uint256[] calldata _ids)
        external
        override(IVault)
        nonReentrant
        whenNotExitPaused
    {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        if (totalPrincipal > totalUnderlyingMinusSponsored())
            revert VaultCannotWithdrawWhenYieldNegative();

        _withdrawAll(_to, _ids, false);
    }

    /// @inheritdoc IVault
    function forceWithdraw(address _to, uint256[] calldata _ids)
        external
        nonReentrant
        whenNotExitPaused
    {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        _withdrawAll(_to, _ids, true);
    }

    function partialWithdraw(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _amounts
    ) external nonReentrant whenNotExitPaused {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        _withdrawPartial(_to, _ids, _amounts);
    }

    /// @inheritdoc IVault
    function investState()
        public
        view
        override(IVault)
        returns (uint256 maxInvestableAmount, uint256 alreadyInvested)
    {
        if (address(strategy) == address(0)) {
            return (0, 0);
        }

        maxInvestableAmount = totalUnderlying().pctOf(investPct);
        alreadyInvested = strategy.investedAssets();
    }

    /// @inheritdoc IVault
    function updateInvested() external override(IVault) onlyKeeper {
        if (address(strategy) == address(0)) revert VaultStrategyNotSet();

        (uint256 maxInvestableAmount, uint256 alreadyInvested) = investState();

        if (maxInvestableAmount == alreadyInvested) revert VaultNothingToDo();

        // disinvest
        if (alreadyInvested > maxInvestableAmount) {
            uint256 disinvestAmount = alreadyInvested - maxInvestableAmount;

            if (disinvestAmount < rebalanceMinimum)
                revert VaultNotEnoughToRebalance();

            strategy.withdrawToVault(disinvestAmount);

            emit Disinvested(disinvestAmount);

            return;
        }

        // invest
        uint256 investAmount = maxInvestableAmount - alreadyInvested;

        if (investAmount < rebalanceMinimum) revert VaultNotEnoughToRebalance();

        underlying.safeTransfer(address(strategy), investAmount);

        strategy.invest();

        emit Invested(investAmount);
    }

    /// @inheritdoc IVault
    function withdrawPerformanceFee() external override(IVault) onlyKeeper {
        uint256 _perfFee = accumulatedPerfFee;
        if (_perfFee == 0) revert VaultNoPerformanceFee();

        accumulatedPerfFee = 0;
        _rebalanceBeforeWithdrawing(_perfFee);

        emit FeeWithdrawn(_perfFee);
        underlying.safeTransfer(treasury, _perfFee);
    }

    //
    // IVaultSponsoring
    //

    /// @inheritdoc IVaultSponsoring
    function sponsor(
        address _inputToken,
        uint256 _amount,
        uint256 _lockDuration,
        uint256 _slippage
    )
        external
        override(IVaultSponsoring)
        nonReentrant
        onlySponsor
        whenNotPaused
    {
        if (_amount == 0) revert VaultCannotSponsor0();

        if (
            _lockDuration < MIN_SPONSOR_LOCK_DURATION ||
            _lockDuration > MAX_SPONSOR_LOCK_DURATION
        ) revert VaultInvalidLockPeriod();

        uint256 lockedUntil = _lockDuration + block.timestamp;
        _depositTokenIds.increment();
        uint256 tokenId = _depositTokenIds.current();

        _transferAndCheckInputToken(msg.sender, _inputToken, _amount);
        uint256 underlyingAmount = _swapIntoUnderlying(
            _inputToken,
            _amount,
            _slippage
        );

        deposits[tokenId] = Deposit(
            underlyingAmount,
            msg.sender,
            address(0),
            lockedUntil
        );
        totalSponsored += underlyingAmount;

        emit Sponsored(tokenId, underlyingAmount, msg.sender, lockedUntil);
    }

    /// @inheritdoc IVaultSponsoring
    function unsponsor(address _to, uint256[] calldata _ids)
        external
        nonReentrant
        whenNotExitPaused
    {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        _unsponsor(_to, _ids);
    }

    /// @inheritdoc IVaultSponsoring
    function partialUnsponsor(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _amounts
    ) external nonReentrant whenNotExitPaused {
        if (_to == address(0)) revert VaultDestinationCannotBe0Address();

        _partialUnsponsor(_to, _ids, _amounts);
    }

    //
    // CurveSwapper
    //

    /// @inheritdoc CurveSwapper
    function getUnderlying()
        public
        view
        override(CurveSwapper)
        returns (address)
    {
        return address(underlying);
    }

    /// Adds a new curve swap pool from an input token to {underlying}
    ///
    /// @param _param Swap pool params
    function addPool(SwapPoolParam memory _param) external onlyAdmin {
        _addPool(_param);
    }

    /// Removes an existing swap pool, and the ability to deposit the given token as underlying
    ///
    /// @param _inputToken the token to remove
    function removePool(address _inputToken) external onlyAdmin {
        _removePool(_inputToken);
    }

    //
    // Admin functions
    //

    /// @inheritdoc IVaultSettings
    function setInvestPct(uint16 _investPct)
        external
        override(IVaultSettings)
        onlySettings
    {
        if (!PercentMath.validPct(_investPct)) revert VaultInvalidInvestpct();

        emit InvestPctUpdated(_investPct);

        investPct = _investPct;
    }

    /// @inheritdoc IVaultSettings
    function setTreasury(address _treasury)
        external
        override(IVaultSettings)
        onlySettings
    {
        if (address(_treasury) == address(0x0))
            revert VaultTreasuryCannotBe0Address();
        treasury = _treasury;
        emit TreasuryUpdated(_treasury);
    }

    /// @inheritdoc IVaultSettings
    function setPerfFeePct(uint16 _perfFeePct)
        external
        override(IVaultSettings)
        onlySettings
    {
        if (!PercentMath.validPct(_perfFeePct))
            revert VaultInvalidPerformanceFee();
        perfFeePct = _perfFeePct;
        emit PerfFeePctUpdated(_perfFeePct);
    }

    /// @inheritdoc IVaultSettings
    function setStrategy(address _strategy)
        external
        override(IVaultSettings)
        onlySettings
    {
        if (_strategy == address(0)) revert VaultStrategyNotSet();
        if (IStrategy(_strategy).vault() != address(this))
            revert VaultInvalidVault();
        if (address(strategy) != address(0) && strategy.hasAssets())
            revert VaultStrategyHasInvestedFunds();

        strategy = IStrategy(_strategy);

        emit StrategyUpdated(_strategy);
    }

    /// @inheritdoc IVaultSettings
    function setLossTolerancePct(uint16 pct)
        external
        override(IVaultSettings)
        onlySettings
    {
        if (!pct.validPct()) revert VaultInvalidLossTolerance();

        lossTolerancePct = pct;
        emit LossTolerancePctUpdated(pct);
    }

    //
    // Public API
    //

    /**
     * Computes the total amount of principal + yield currently controlled by the
     * vault and the strategy. The principal + yield is the total amount
     * of underlying that can be claimed or withdrawn, excluding the sponsored amount and performance fee.
     *
     * @return Total amount of principal and yield help by the vault (not including sponsored amount and performance fee).
     */
    function totalUnderlyingMinusSponsored() public view returns (uint256) {
        uint256 _totalUnderlying = totalUnderlying();
        uint256 deductAmount = totalSponsored + accumulatedPerfFee;
        if (deductAmount > _totalUnderlying) {
            return 0;
        }

        return _totalUnderlying - deductAmount;
    }

    //
    // ERC165
    //

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC165, AccessControl)
        returns (bool)
    {
        return
            interfaceId == type(IVault).interfaceId ||
            interfaceId == type(IVaultSponsoring).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    //
    // Internal API
    //

    /**
     * Withdraws the principal from the deposits with the ids provided in @param _ids and sends it to @param _to.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     * @param _force Boolean to specify if the action should be perfomed when there's loss.
     */
    function _withdrawAll(
        address _to,
        uint256[] calldata _ids,
        bool _force
    ) internal {
        uint256 localTotalShares = totalShares;
        uint256 localTotalPrincipal = totalUnderlyingMinusSponsored();
        uint256 amount;
        uint256 idsLen = _ids.length;

        for (uint256 i = 0; i < idsLen; ++i) {
            uint256 depositAmount = deposits[_ids[i]].amount;

            amount += _withdrawSingle(
                _ids[i],
                localTotalShares,
                localTotalPrincipal,
                _to,
                _force,
                depositAmount
            );
        }

        _rebalanceBeforeWithdrawing(amount);

        underlying.safeTransfer(_to, amount);
    }

    function _withdrawPartial(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _amounts
    ) internal {
        uint256 localTotalShares = totalShares;
        uint256 localTotalPrincipal = totalUnderlyingMinusSponsored();
        uint256 amount;
        uint256 idsLen = _ids.length;

        for (uint256 i = 0; i < idsLen; ++i) {
            amount += _withdrawSingle(
                _ids[i],
                localTotalShares,
                localTotalPrincipal,
                _to,
                false,
                _amounts[i]
            );
        }

        _rebalanceBeforeWithdrawing(amount);

        underlying.safeTransfer(_to, amount);
    }

    /**
     * Rebalances the vault's funds to cover the transfer of funds from the vault
     * by disinvesting from the strategy. After the rebalance the vault is left
     * with a set percentage (100% - invest%) of the total underlying as reserves.
     *
     * @notice this will have effect only for sync strategies.
     *
     * @param _amount Funds to be transferred from the vault.
     */
    function _rebalanceBeforeWithdrawing(uint256 _amount) internal {
        uint256 vaultBalance = underlying.balanceOf(address(this));

        if (_amount <= vaultBalance) return;
        if (!strategy.isSync()) revert VaultNotEnoughFunds();

        uint256 expectedReserves = (totalUnderlying() - _amount).pctOf(
            10000 - investPct
        );

        // we want to withdraw the from the strategy only what is needed
        // to cover the transfer and leave the vault with the expected reserves
        uint256 needed = _amount + expectedReserves - vaultBalance;

        strategy.withdrawToVault(needed);

        emit Disinvested(needed);
    }

    /**
     * Withdraws the sponsored amount for the deposits with the ids provided
     * in @param _ids and sends it to @param _to.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function _unsponsor(address _to, uint256[] calldata _ids) internal {
        uint256 sponsorAmount;
        uint256 idsLen = _ids.length;

        for (uint8 i = 0; i < idsLen; ++i) {
            uint256 tokenId = _ids[i];
            uint256 amount = deposits[tokenId].amount;

            _unsponsorSingle(_to, tokenId, amount);

            sponsorAmount += amount;
        }

        _decreaseTotalSponsoredAndTransfer(_to, sponsorAmount);
    }

    /**
     * Withdraws the specified sponsored amounts @param _amounts for the deposits with the ids provided
     * in @param _ids and sends it to @param _to.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     * @param _amounts Array with the amounts to withdraw.
     */
    function _partialUnsponsor(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _amounts
    ) internal {
        uint256 sponsorAmount;
        uint256 idsLen = _ids.length;

        for (uint8 i = 0; i < idsLen; ++i) {
            uint256 depositId = _ids[i];
            uint256 amount = _amounts[i];

            _unsponsorSingle(_to, depositId, amount);

            sponsorAmount += amount;
        }

        _decreaseTotalSponsoredAndTransfer(_to, sponsorAmount);
    }

    /**
     * Validates conditions for unsponsoring amount @param _amount of the deposit with the id @param _id.
     *
     * @param _to Address that will receive the funds.
     * @param _tokenId Id of the deposit.
     * @param _amount Amount to be unsponsored/withdrawn.
     */
    function _unsponsorSingle(
        address _to,
        uint256 _tokenId,
        uint256 _amount
    ) internal {
        Deposit memory _deposit = deposits[_tokenId];

        if (_deposit.owner != msg.sender) revert VaultNotAllowed();
        if (_deposit.lockedUntil > block.timestamp) revert VaultAmountLocked();
        if (_deposit.claimerId != address(0)) revert VaultNotSponsor();
        if (_deposit.amount < _amount)
            revert VaultCannotWithdrawMoreThanAvailable();

        bool isFull = _amount == _deposit.amount;

        emit Unsponsored(_tokenId, _amount, _to, isFull);

        if (!isFull) {
            deposits[_tokenId].amount -= _amount;
            return;
        }

        delete deposits[_tokenId];
    }

    /**
     * Updates totalSponsored by subtracting the amount @param _amount and performing a transfer to @param _to.
     *
     * @param _to Adress that will receive the funds.
     * @param _amount Amount being unsponsored.
     */
    function _decreaseTotalSponsoredAndTransfer(address _to, uint256 _amount)
        internal
    {
        if (_amount > totalUnderlying()) revert VaultNotEnoughFunds();

        totalSponsored -= _amount;

        _rebalanceBeforeWithdrawing(_amount);

        underlying.safeTransfer(_to, _amount);
    }

    /**
     * @dev `_createDeposit` declares too many locals
     * We move some of them to this struct to fix the problem
     */
    struct CreateDepositLocals {
        uint256 totalShares;
        uint256 totalUnderlying;
        uint16 accumulatedPct;
        uint256 accumulatedAmount;
        uint256 claimsLen;
    }

    /**
     * Creates a deposit with the given amount of underlying and claim
     * structure. The deposit is locked until the timestamp specified in @param _lockedUntil.
     * @notice This function assumes underlying will be transfered elsewhere in
     * the transaction.
     *
     * @notice Underlying must be transfered *after* this function, in order to
     * correctly calculate shares.
     *
     * @notice claims must add up to 100%.
     *
     * @param _amount Amount of underlying to consider @param claims claim
     * @param _lockedUntil Timestamp at which the deposit unlocks
     * @param claims Claim params
     * params.
     */
    function _createDeposit(
        uint256 _previousTotalUnderlying,
        uint256 _amount,
        uint64 _lockedUntil,
        ClaimParams[] calldata claims,
        string calldata _name,
        uint256 _groupId
    ) internal returns (uint256[] memory) {
        CreateDepositLocals memory locals = CreateDepositLocals({
            totalShares: totalShares,
            totalUnderlying: _previousTotalUnderlying,
            accumulatedPct: 0,
            accumulatedAmount: 0,
            claimsLen: claims.length
        });

        uint256[] memory result = new uint256[](locals.claimsLen);

        for (uint256 i = 0; i < locals.claimsLen; ++i) {
            ClaimParams memory data = claims[i];
            if (data.pct == 0) revert VaultClaimPercentageCannotBe0();
            if (data.beneficiary == address(0)) revert VaultClaimerCannotBe0();
            // if it's the last claim, just grab all remaining amount, instead
            // of relying on percentages
            uint256 localAmount = i == locals.claimsLen - 1
                ? _amount - locals.accumulatedAmount
                : _amount.pctOf(data.pct);

            result[i] = _createClaim(
                _groupId,
                localAmount,
                _lockedUntil,
                data,
                locals.totalShares,
                locals.totalUnderlying,
                _name
            );
            locals.accumulatedPct += data.pct;
            locals.accumulatedAmount += localAmount;
        }

        if (!locals.accumulatedPct.is100Pct()) revert VaultClaimsDontAddUp();

        return result;
    }

    /**
     * @dev `_createClaim` declares too many locals
     * We move some of them to this struct to fix the problem
     */
    struct CreateClaimLocals {
        uint256 newShares;
        address claimerId;
        uint256 tokenId;
    }

    function _createClaim(
        uint256 _depositGroupId,
        uint256 _amount,
        uint64 _lockedUntil,
        ClaimParams memory _claim,
        uint256 _localTotalShares,
        uint256 _localTotalPrincipal,
        string calldata _name
    ) internal returns (uint256) {
        _depositTokenIds.increment();
        CreateClaimLocals memory locals = CreateClaimLocals({
            newShares: _computeShares(
                _amount,
                _localTotalShares,
                _localTotalPrincipal
            ),
            claimerId: _claim.beneficiary,
            tokenId: _depositTokenIds.current()
        });

        // Checks if the user is not already in debt
        if (
            _computeShares(
                _applyLossTolerance(claimer[locals.claimerId].totalPrincipal),
                _localTotalShares,
                _localTotalPrincipal
            ) > claimer[locals.claimerId].totalShares
        ) revert VaultCannotDepositWhenClaimerInDebt();

        claimer[locals.claimerId].totalShares += locals.newShares;
        claimer[locals.claimerId].totalPrincipal += _amount;

        totalShares += locals.newShares;
        totalPrincipal += _amount;

        deposits[locals.tokenId] = Deposit(
            _amount,
            msg.sender,
            locals.claimerId,
            _lockedUntil
        );

        emit DepositMinted(
            locals.tokenId,
            _depositGroupId,
            _amount,
            locals.newShares,
            msg.sender,
            _claim.beneficiary,
            locals.claimerId,
            _lockedUntil,
            _claim.data,
            _name
        );

        return locals.tokenId;
    }

    /**
     * Reduces the principal and shares of the claimer.
     * If there were any yield to be claimed, the claimer will also keep shares to withdraw later on.
     *
     * @notice This function doesn't transfer any funds, it only updates the state.
     *
     * @notice Only the owner of the deposit may call this function.
     *
     * @param _tokenId The deposit ID to withdraw from.
     * @param _totalShares The total shares to consider for the withdraw.
     * @param _totalUnderlyingMinusSponsored The total underlying to consider for the withdraw.
     * @param _to Where the funds will be sent
     * @param _force If the withdraw should still withdraw if there are not enough funds in the vault.
     *
     * @return the amount to withdraw.
     */
    function _withdrawSingle(
        uint256 _tokenId,
        uint256 _totalShares,
        uint256 _totalUnderlyingMinusSponsored,
        address _to,
        bool _force,
        uint256 _amount
    ) internal returns (uint256) {
        if (deposits[_tokenId].owner != msg.sender)
            revert VaultNotOwnerOfDeposit();

        // memoizing saves warm sloads
        Deposit memory _deposit = deposits[_tokenId];
        Claimer memory _claim = claimer[_deposit.claimerId];

        if (_deposit.lockedUntil > block.timestamp) revert VaultDepositLocked();
        if (_deposit.claimerId == address(0)) revert VaultNotDeposit();
        if (_deposit.amount < _amount)
            revert VaultCannotWithdrawMoreThanAvailable();

        // Amount of shares the _amount is worth
        uint256 amountShares = _computeShares(
            _amount,
            _totalShares,
            _totalUnderlyingMinusSponsored
        );

        // Amount of shares the _amount is worth taking in the claimer's
        // totalShares and totalPrincipal
        uint256 claimerShares = (_amount * _claim.totalShares) /
            _claim.totalPrincipal;

        if (!_force && amountShares > claimerShares)
            revert VaultMustUseForceWithdrawToAcceptLosses();

        uint256 sharesToBurn = amountShares;

        if (_force && amountShares > claimerShares)
            sharesToBurn = claimerShares;

        claimer[_deposit.claimerId].totalShares -= sharesToBurn;
        claimer[_deposit.claimerId].totalPrincipal -= _amount;

        totalShares -= sharesToBurn;
        totalPrincipal -= _amount;

        bool isFull = _deposit.amount == _amount;

        if (isFull) {
            delete deposits[_tokenId];
        } else {
            deposits[_tokenId].amount -= _amount;
        }

        uint256 amount = _computeAmount(
            sharesToBurn,
            _totalShares,
            _totalUnderlyingMinusSponsored
        );

        emit DepositWithdrawn(_tokenId, sharesToBurn, amount, _to, isFull);

        return amount;
    }

    function _transferAndCheckInputToken(
        address _from,
        address _token,
        uint256 _amount
    ) internal {
        uint256 balanceBefore = IERC20(_token).balanceOf(address(this));
        IERC20(_token).safeTransferFrom(_from, address(this), _amount);
        uint256 balanceAfter = IERC20(_token).balanceOf(address(this));

        if (balanceAfter != balanceBefore + _amount)
            revert VaultAmountDoesNotMatchParams();
    }

    function _blockTimestamp() internal view returns (uint64) {
        return uint64(block.timestamp);
    }

    /**
     * Computes amount of shares that will be received for a given deposit amount
     *
     * @param _amount Amount of deposit to consider.
     * @param _totalShares Amount of existing shares to consider.
     * @param _totalUnderlyingMinusSponsored Amount of existing underlying to consider.
     * @return Amount of shares the deposit will receive.
     */
    function _computeShares(
        uint256 _amount,
        uint256 _totalShares,
        uint256 _totalUnderlyingMinusSponsored
    ) internal pure returns (uint256) {
        if (_amount == 0) return 0;
        if (_totalShares == 0) return _amount * SHARES_MULTIPLIER;
        if (_totalUnderlyingMinusSponsored == 0)
            revert VaultCannotComputeSharesWithoutPrincipal();

        return (_amount * _totalShares) / _totalUnderlyingMinusSponsored;
    }

    /**
     * Computes the amount of underlying from a given number of shares
     *
     * @param _shares Number of shares.
     * @param _totalShares Amount of existing shares to consider.
     * @param _totalUnderlyingMinusSponsored Amounf of existing underlying to consider.
     * @return Amount that corresponds to the number of shares.
     */
    function _computeAmount(
        uint256 _shares,
        uint256 _totalShares,
        uint256 _totalUnderlyingMinusSponsored
    ) internal pure returns (uint256) {
        if (
            _shares == 0 ||
            _totalShares == 0 ||
            _totalUnderlyingMinusSponsored == 0
        ) {
            return 0;
        }

        return ((_totalUnderlyingMinusSponsored * _shares) / _totalShares);
    }

    /**
     * Applies a loss tolerance to the given @param _amount.
     *
     * This function is used to prevent the vault from entering loss mode when funds are lost due to fees in the strategy.
     * For instance, the fees taken by Anchor.
     *
     * @param _amount Amount to apply the fees to.
     *
     * @return Amount with the fees applied.
     */
    function _applyLossTolerance(uint256 _amount)
        internal
        view
        returns (uint256)
    {
        return _amount - _amount.pctOf(lossTolerancePct);
    }

    function sharesOf(address claimerId) external view returns (uint256) {
        return claimer[claimerId].totalShares;
    }

    function principalOf(address claimerId) external view returns (uint256) {
        return claimer[claimerId].totalPrincipal;
    }

    function pause() external onlyAdmin {
        _pause();
    }

    function unpause() external onlyAdmin {
        _unpause();
    }

    function exitPause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _exitPause();
    }

    function exitUnpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _exitUnpause();
    }
}

File 2 of 24 : 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 3 of 24 : 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 4 of 24 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-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 5 of 24 : 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 6 of 24 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 7 of 24 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 8 of 24 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 9 of 24 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 10 of 24 : Counters.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 11 of 24 : IVault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IVault {
    //
    // Structs
    //

    struct ClaimParams {
        uint16 pct;
        address beneficiary;
        bytes data;
    }

    struct DepositParams {
        address inputToken;
        uint64 lockDuration;
        uint256 amount;
        ClaimParams[] claims;
        string name;
        uint256 slippage;
    }

    struct Deposit {
        /// amount of the deposit
        uint256 amount;
        /// wallet of the owner
        address owner;
        /// wallet of the claimer
        address claimerId;
        /// when can the deposit be withdrawn
        uint256 lockedUntil;
    }

    struct Claimer {
        uint256 totalPrincipal;
        uint256 totalShares;
    }

    //
    // Events
    //

    event DepositMinted(
        uint256 indexed id,
        uint256 groupId,
        uint256 amount,
        uint256 shares,
        address indexed depositor,
        address indexed claimer,
        address claimerId,
        uint64 lockedUntil,
        bytes data,
        string name
    );

    event DepositWithdrawn(
        uint256 indexed id,
        uint256 shares,
        uint256 amount,
        address indexed to,
        bool burned
    );

    event Invested(uint256 amount);

    event Disinvested(uint256 amount);

    event YieldClaimed(
        address claimerId,
        address indexed to,
        uint256 amount,
        uint256 burnedShares,
        uint256 perfFee,
        uint256 totalUnderlying,
        uint256 totalShares
    );

    event FeeWithdrawn(uint256 amount);

    //
    // Public API
    //

    /**
     * Update the invested amount;
     */
    function updateInvested() external;

    /**
     * Calculate maximum investable amount and already invested amount
     *
     * @return maxInvestableAmount maximum investable amount
     * @return alreadyInvested already invested amount
     */
    function investState()
        external
        view
        returns (uint256 maxInvestableAmount, uint256 alreadyInvested);

    /**
     * Percentage of the total underlying to invest in the strategy
     */
    function investPct() external view returns (uint16);

    /**
     * Underlying ERC20 token accepted by the vault
     */
    function underlying() external view returns (IERC20Metadata);

    /**
     * Minimum lock period for each deposit
     */
    function minLockPeriod() external view returns (uint64);

    /**
     * Total amount of underlying currently controlled by the
     * vault and the its strategy.
     */
    function totalUnderlying() external view returns (uint256);

    /**
     * Total amount of shares
     */
    function totalShares() external view returns (uint256);

    /**
     * Computes the amount of yield available for an an address.
     *
     * @param _to address to consider.
     *
     * @return claimable yield for @param _to, share of generated yield by @param _to,
     *      and performance fee from generated yield
     */
    function yieldFor(address _to)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );

    /**
     * Accumulate performance fee and transfers rest yield generated for the caller to
     *
     * @param _to Address that will receive the yield.
     */
    function claimYield(address _to) external;

    /**
     * Creates a new deposit using the specified group id
     *
     * @param _groupId The group id for the new deposit
     * @param _params Deposit params
     */
    function depositForGroupId(uint256 _groupId, DepositParams calldata _params)
        external
        returns (uint256[] memory);

    /**
     * Creates a new deposit
     *
     * @param _params Deposit params
     */
    function deposit(DepositParams calldata _params)
        external
        returns (uint256[] memory);

    /**
     * Withdraws the principal from the deposits with the ids provided in @param _ids and sends it to @param _to.
     *
     * It fails if the vault is underperforming and there are not enough funds
     * to withdraw the expected amount.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function withdraw(address _to, uint256[] calldata _ids) external;

    /**
     * Withdraws the principal from the deposits with the ids provided in @param _ids and sends it to @param _to.
     *
     * When the vault is underperforming it withdraws the funds with a loss.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function forceWithdraw(address _to, uint256[] calldata _ids) external;

    /**
     * Withdraws any pending performance fee amount back to the treasury
     */
    function withdrawPerformanceFee() external;
}

File 12 of 24 : IVaultSponsoring.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

interface IVaultSponsoring {
    //
    // Events
    //

    /// Emitted when a new sponsor deposit is created
    event Sponsored(
        uint256 indexed id,
        uint256 amount,
        address indexed depositor,
        uint256 lockedUntil
    );

    /// Emitted when an existing sponsor withdraws
    event Unsponsored(
        uint256 indexed id,
        uint256 amount,
        address indexed to,
        bool burned
    );

    /**
     * Total amount currently sponsored
     */
    function totalSponsored() external view returns (uint256);

    /**
     * Creates a sponsored deposit with the amount provided in @param _amount.
     * Sponsored amounts will be invested like deposits, but unlike deposits
     * there are no claimers and the yield generated is donated to the vault.
     * The amount is locked until the timestamp specified in @param _lockedUntil.
     *
     * @param _inputToken The input token to deposit.
     * @param _amount Amount to sponsor.
     * @param _lockedUntil When the sponsor can unsponsor the amount.
     */
    function sponsor(
        address _inputToken,
        uint256 _amount,
        uint256 _lockedUntil,
        uint256 _slippage
    ) external;

    /**
     * Withdraws the sponsored amount for the deposits with the ids provided
     * in @param _ids and sends it to @param _to.
     *
     * It fails if the vault is underperforming and there are not enough funds
     * to withdraw the sponsored amount.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     */
    function unsponsor(address _to, uint256[] calldata _ids) external;

    /**
     * Withdraws the specified sponsored amounts @param _amounts for the deposits with the ids provided
     * in @param _ids and sends it to @param _to.
     *
     * @notice fails if there are not enough funds to withdraw the specified amounts.
     *
     * @param _to Address that will receive the funds.
     * @param _ids Array with the ids of the deposits.
     * @param _amounts Array with the amounts to withdraw.
     */
    function partialUnsponsor(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _amounts
    ) external;
}

File 13 of 24 : IVaultSettings.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

interface IVaultSettings {
    //
    // Events
    //

    event InvestPctUpdated(uint256 percentage);
    event TreasuryUpdated(address indexed treasury);
    event PerfFeePctUpdated(uint16 pct);
    event StrategyUpdated(address indexed strategy);
    event LossTolerancePctUpdated(uint16 pct);

    /**
     * Update invest percentage
     *
     * Emits {InvestPercentageUpdated} event
     *
     * @param _investPct the new invest percentage
     */
    function setInvestPct(uint16 _investPct) external;

    /**
     * Changes the treasury used by the vault.
     *
     * @param _treasury the new strategy's address.
     */
    function setTreasury(address _treasury) external;

    /**
     * Changes the performance fee used by the vault.
     *
     * @param _perfFeePct the new performance fee.
     */
    function setPerfFeePct(uint16 _perfFeePct) external;

    /**
     * Changes the strategy used by the vault.
     *
     * @notice if there is invested funds in previous strategy, it is not allowed to set new strategy.
     * @param _strategy the new strategy's address.
     */
    function setStrategy(address _strategy) external;

    /**
     * Changes the estimated investment fee used by the strategy.
     *
     * @param _pct the new investment fee estimated percentage.
     */
    function setLossTolerancePct(uint16 _pct) external;
}

File 14 of 24 : CurveSwapper.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {ICurve} from "../interfaces/curve/ICurve.sol";

/// Helper abstract contract to manage curve swaps
abstract contract CurveSwapper {
    using SafeERC20 for IERC20;

    //
    // Structs
    //

    struct Swapper {
        /// Curve pool instance
        ICurve pool;
        /// decimals in token
        uint8 tokenDecimals;
        /// decimals in underlying
        uint8 underlyingDecimals;
        /// index of the deposit token we want to exchange to/from underlying
        int128 tokenI;
        /// index of underlying used by the vault (presumably always UST)
        int128 underlyingI;
    }

    struct SwapPoolParam {
        address token;
        address pool;
        int128 tokenI;
        int128 underlyingI;
    }

    //
    // Events
    //

    /// Emitted when a new swap pool is added
    event CurveSwapPoolAdded(
        address indexed token,
        address indexed pool,
        int128 tokenI,
        int128 underlyingI
    );

    /// Emitted when a swap pool is removed
    event CurveSwapPoolRemoved(address indexed token);

    /// Emitted after every swap
    event Swap(
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount
    );

    //
    // State
    //

    /// token => curve pool (for trading token/underlying)
    mapping(address => Swapper) public swappers;

    /// @return The address of the vault's main underlying token
    function getUnderlying() public view virtual returns (address);

    /// Swaps a given amount of
    /// Only works if the pool has previously been inserted into the contract
    ///
    /// @param _token The token we want to swap into
    /// @param _amount The amount of underlying we want to swap
    function _swapIntoUnderlying(
        address _token,
        uint256 _amount,
        uint256 _slippage
    ) internal returns (uint256 amount) {
        address underlyingToken = getUnderlying();
        if (_token == underlyingToken) {
            // same token, nothing to do
            return _amount;
        }

        Swapper storage swapper = swappers[_token];
        require(
            address(swapper.pool) != address(0x0),
            "non-existing swap pool"
        );

        uint256 minAmount = _calcMinDy(
            _amount,
            swapper.tokenDecimals,
            swapper.underlyingDecimals,
            _slippage
        );

        amount = swapper.pool.exchange_underlying(
            swapper.tokenI,
            swapper.underlyingI,
            _amount,
            minAmount
        );

        emit Swap(_token, underlyingToken, _amount, amount);
    }

    /// Swaps a given amount of Underlying into a given token
    /// Only works if the pool has previously been inserted into the contract
    ///
    /// @param _token The token we want to swap into
    /// @param _amount The amount of underlying we want to swap
    function _swapFromUnderlying(
        address _token,
        uint256 _amount,
        uint256 _slippage
    ) internal returns (uint256 amount) {
        if (_token == getUnderlying()) {
            // same token, nothing to do
            return _amount;
        }

        Swapper storage swapper = swappers[_token];

        uint256 minAmount = _calcMinDy(
            _amount,
            swapper.underlyingDecimals,
            swapper.tokenDecimals,
            _slippage
        );

        amount = swapper.pool.exchange_underlying(
            swapper.underlyingI,
            swapper.tokenI,
            _amount,
            minAmount
        );

        emit Swap(getUnderlying(), _token, _amount, amount);
    }

    function _calcMinDy(
        uint256 _amount,
        uint8 _fromDecimals,
        uint8 _toDecimals,
        uint256 _slippage
    ) internal pure returns (uint256) {
        return
            (_amount * _slippage * 10**_toDecimals) / (10**_fromDecimals * 10000);
    }

    /// This is necessary because some tokens (USDT) force you to approve(0)
    /// before approving a new amount meaning if we always approved blindly,
    /// then we could get random failures on the second attempt
    function _approveIfNecessary(address _token, address _pool) internal {
        uint256 allowance = IERC20(_token).allowance(address(this), _pool);

        if (allowance == 0) {
            IERC20(_token).safeApprove(_pool, type(uint256).max);
        }
    }

    /// @param _swapPools configs for each swap pool
    function _addPools(SwapPoolParam[] memory _swapPools) internal {
        uint256 length = _swapPools.length;
        for (uint256 i = 0; i < length; ++i) {
            _addPool(_swapPools[i]);
        }
    }

    function _addPool(SwapPoolParam memory _param) internal {
        require(
            address(swappers[_param.token].pool) == address(0),
            "token already has a swap pool"
        );
        require(
            ICurve(_param.pool).coins(uint256(uint128(_param.underlyingI))) ==
                getUnderlying(),
            "_underlyingI does not match underlying token"
        );

        uint256 tokenDecimals = IERC20Metadata(_param.token).decimals();
        uint256 underlyingDecimals = IERC20Metadata(getUnderlying()).decimals();

        // TODO check if _token and _underlyingIndex match the pool settings
        swappers[_param.token] = Swapper(
            ICurve(_param.pool),
            uint8(tokenDecimals),
            uint8(underlyingDecimals),
            _param.tokenI,
            _param.underlyingI
        );

        _approveIfNecessary(getUnderlying(), address(_param.pool));
        _approveIfNecessary(_param.token, address(_param.pool));

        emit CurveSwapPoolAdded(
            _param.token,
            _param.pool,
            _param.tokenI,
            _param.underlyingI
        );
    }

    function _removePool(address _inputToken) internal {
        require(
            address(swappers[_inputToken].pool) != address(0),
            "pool does not exist"
        );
        delete swappers[_inputToken];

        emit CurveSwapPoolRemoved(_inputToken);
    }
}

File 15 of 24 : PercentMath.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

library PercentMath {
    // Divisor used for representing percentages
    uint256 public constant PCT_DIVISOR = 10000;

    /**
     * @dev Returns whether an amount is a valid percentage out of PCT_DIVISOR
     * @param _amount Amount that is supposed to be a percentage
     */
    function validPct(uint256 _amount) internal pure returns (bool) {
        return _amount <= PCT_DIVISOR;
    }

    /**
     * @dev Compute percentage of a value with the percentage represented by a fraction over PERC_DIVISOR
     * @param _amount Amount to take the percentage of
     * @param _fracNum Numerator of fraction representing the percentage with PCT_DIVISOR as the denominator
     */
    function pctOf(uint256 _amount, uint16 _fracNum)
        internal
        pure
        returns (uint256)
    {
        return (_amount * _fracNum) / PCT_DIVISOR;
    }

    /**
     * @dev Checks if a given number corresponds to 100%
     * @param _perc Percentage value to check, with PCT_DIVISOR
     */
    function is100Pct(uint256 _perc) internal pure returns (bool) {
        return _perc == PCT_DIVISOR;
    }
}

File 16 of 24 : ExitPausable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an exit stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotExitPaused` and `whenExitPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract ExitPausable is Context {
    /**
     * @dev Emitted when the exitPause is triggered by `account`.
     */
    event ExitPaused(address account);

    /**
     * @dev Emitted when the exitPause is lifted by `account`.
     */
    event ExitUnpaused(address account);

    bool private _exitPaused;

    /**
     * @dev Initializes the contract in exitUnpaused state.
     */
    constructor() {
        _exitPaused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not exitPaused.
     *
     * Requirements:
     *
     * - The contract must not be exitPaused.
     */
    modifier whenNotExitPaused() {
        _requireNotExitPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is exitPaused.
     *
     * Requirements:
     *
     * - The contract must be exitPaused.
     */
    modifier whenExitPaused() {
        _requireExitPaused();
        _;
    }

    /**
     * @dev Returns true if the contract is exitPaused, and false otherwise.
     */
    function exitPaused() public view virtual returns (bool) {
        return _exitPaused;
    }

    /**
     * @dev Throws if the contract is exitPaused.
     */
    function _requireNotExitPaused() internal view virtual {
        require(!exitPaused(), "Pausable: ExitPaused");
    }

    /**
     * @dev Throws if the contract is not exitPaused.
     */
    function _requireExitPaused() internal view virtual {
        require(exitPaused(), "Pausable: not ExitPaused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be exitPaused.
     */
    function _exitPause() internal virtual whenNotExitPaused {
        _exitPaused = true;
        emit ExitPaused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be exitPaused.
     */
    function _exitUnpause() internal virtual whenExitPaused {
        _exitPaused = false;
        emit ExitUnpaused(_msgSender());
    }
}

File 17 of 24 : IStrategy.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * IStrategy defines the interface for pluggable contracts used by vaults to invest funds and generate yield.
 *
 * @notice It's up to the strategy to decide what do to with investable assets provided by a vault.
 *
 * @notice It's up to the vault to decide how much to invest/disinvest from the total pool.
 */
interface IStrategy {
    /**
     * Emmited when funds are invested by the strategy.
     *
     *@param amount amount invested
     */
    event StrategyInvested(uint256 amount);
    /**
     * Emmited when funds are withdrawn (disinvested) by the strategy.
     *
     *@param amount amount withdrawn
     */
    event StrategyWithdrawn(uint256 amount);

    /**
     * Provides information about wether the strategy is synchronous or asynchronous.
     *
     * @notice Synchronous strategies support instant withdrawals,
     * while asynchronous strategies impose a delay before withdrawals can be made.
     *
     * @return true if the strategy is synchronous, false otherwise
     */
    function isSync() external view returns (bool);

    /**
     * The vault linked to this strategy.
     *
     * @return The vault's address
     */
    function vault() external view returns (address);

    /**
     * Withdraws the specified amount back to the vault (disinvests)
     *
     * @param amount Amount to withdraw
     */
    function withdrawToVault(uint256 amount) external;

    /**
     * Amount of the underlying currency currently invested by the strategy.
     *
     * @notice both held and invested amounts are included here, using the
     * latest known exchange rates to the underlying currency
     *
     * @return The total amount of underlying
     */
    function investedAssets() external view returns (uint256);

    /**
     * Indicates if assets are invested into strategy or not.
     *
     * @notice this will be used when removing the strategy from the vault
     * @return true if assets invested, false if nothing invested.
     */
    function hasAssets() external view returns (bool);

    /**
     * Deposits of all the available underlying into the yield generating protocol.
     */
    function invest() external;
}

File 18 of 24 : CustomErrors.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

interface CustomErrors {
    //
    // Vault Errors
    //

    // Vault: sender is not the owner of the group id
    error VaultSenderNotOwnerOfGroupId();

    // Vault: invalid investPct
    error VaultInvalidInvestpct();

    // Vault: invalid performance fee
    error VaultInvalidPerformanceFee();

    // Vault: no performance fee
    error VaultNoPerformanceFee();

    // Vault: invalid lossTolerance
    error VaultInvalidLossTolerance();

    // Vault: underlying cannot be 0x0
    error VaultUnderlyingCannotBe0Address();

    // Vault: treasury cannot be 0x0
    error VaultTreasuryCannotBe0Address();

    // Vault: admin cannot be 0x0
    error VaultAdminCannotBe0Address();

    // Vault: cannot transfer admin rights to self
    error VaultCannotTransferAdminRightsToSelf();

    // Vault: caller is not admin
    error VaultCallerNotAdmin();

    // Vault: caller is not settings
    error VaultCallerNotSettings();

    // Vault: caller is not keeper
    error VaultCallerNotKeeper();

    // Vault: caller is not sponsor
    error VaultCallerNotSponsor();

    // Vault: destination address is 0x
    error VaultDestinationCannotBe0Address();

    // Vault: strategy is not set
    error VaultStrategyNotSet();

    // Vault: invalid minLockPeriod
    error VaultInvalidMinLockPeriod();

    // Vault: invalid lock period
    error VaultInvalidLockPeriod();

    // Vault: cannot deposit 0
    error VaultCannotDeposit0();

    // Vault: cannot sponsor 0
    error VaultCannotSponsor0();

    // Vault: cannot deposit when yield is negative
    error VaultCannotDepositWhenYieldNegative();

    // Vault: cannot deposit when the claimer is in debt
    error VaultCannotDepositWhenClaimerInDebt();

    // Vault: cannot withdraw when yield is negative
    error VaultCannotWithdrawWhenYieldNegative();

    // Vault: nothing to do
    error VaultNothingToDo();

    // Vault: not enough to rebalance
    error VaultNotEnoughToRebalance();

    // Vault: invalid vault
    error VaultInvalidVault();

    // Vault: strategy has invested funds
    error VaultStrategyHasInvestedFunds();

    // Vault: not enough funds
    error VaultNotEnoughFunds();

    // Vault: you are not allowed
    error VaultNotAllowed();

    // Vault: amount is locked
    error VaultAmountLocked();

    // Vault: deposit is locked
    error VaultDepositLocked();

    // Vault: token id is not a sponsor
    error VaultNotSponsor();

    // Vault: token id is not a deposit
    error VaultNotDeposit();

    // Vault: claim percentage cannot be 0
    error VaultClaimPercentageCannotBe0();

    // Vault: claimer cannot be address 0
    error VaultClaimerCannotBe0();

    // Vault: claims don't add up to 100%
    error VaultClaimsDontAddUp();

    // Vault: you are not the owner of a deposit
    error VaultNotOwnerOfDeposit();

    // Vault: cannot withdraw more than the available amount
    error VaultCannotWithdrawMoreThanAvailable();

    // Vault: must force withdraw to withdraw with a loss
    error VaultMustUseForceWithdrawToAcceptLosses();

    // Vault: amount received does not match params
    error VaultAmountDoesNotMatchParams();

    // Vault: cannot compute shares when there's no principal
    error VaultCannotComputeSharesWithoutPrincipal();

    // Vault: deposit name for MetaVault too short
    error VaultDepositNameTooShort();

    // Vault: no yield to claim
    error VaultNoYieldToClaim();

    //
    // Strategy Errors
    //

    // Strategy: admin is 0x
    error StrategyAdminCannotBe0Address();

    // Strategy: cannot transfer admin rights to self
    error StrategyCannotTransferAdminRightsToSelf();

    // Strategy: underlying is 0x
    error StrategyUnderlyingCannotBe0Address();

    // Strategy: not an IVault
    error StrategyNotIVault();

    // Strategy: caller is not manager
    error StrategyCallerNotManager();

    // Strategy: caller has no settings role
    error StrategyCallerNotSettings();

    // Strategy: caller is not admin
    error StrategyCallerNotAdmin();

    // Strategy: amount is 0
    error StrategyAmountZero();

    // Strategy: not running
    error StrategyNotRunning();

    // Not Enough Underlying Balance in Strategy contract
    error StrategyNoUnderlying();

    // Not Enough Shares in Strategy Contract
    error StrategyNotEnoughShares();
}

File 19 of 24 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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 20 of 24 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library 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
     * ====
     *
     * [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://diligence.consensys.net/posts/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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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 21 of 24 : IERC165.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 IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 22 of 24 : IAccessControl.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 IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 23 of 24 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 24 of 24 : ICurve.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.10;

interface ICurve {
    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy
    ) external returns (uint256);

    function coins(uint256 i) external view returns (address);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20Metadata","name":"_underlying","type":"address"},{"internalType":"uint64","name":"_minLockPeriod","type":"uint64"},{"internalType":"uint16","name":"_investPct","type":"uint16"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint16","name":"_perfFeePct","type":"uint16"},{"internalType":"uint16","name":"_lossTolerancePct","type":"uint16"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"int128","name":"tokenI","type":"int128"},{"internalType":"int128","name":"underlyingI","type":"int128"}],"internalType":"struct CurveSwapper.SwapPoolParam[]","name":"_swapPools","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"StrategyAdminCannotBe0Address","type":"error"},{"inputs":[],"name":"StrategyAmountZero","type":"error"},{"inputs":[],"name":"StrategyCallerNotAdmin","type":"error"},{"inputs":[],"name":"StrategyCallerNotManager","type":"error"},{"inputs":[],"name":"StrategyCallerNotSettings","type":"error"},{"inputs":[],"name":"StrategyCannotTransferAdminRightsToSelf","type":"error"},{"inputs":[],"name":"StrategyNoUnderlying","type":"error"},{"inputs":[],"name":"StrategyNotEnoughShares","type":"error"},{"inputs":[],"name":"StrategyNotIVault","type":"error"},{"inputs":[],"name":"StrategyNotRunning","type":"error"},{"inputs":[],"name":"StrategyUnderlyingCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultAdminCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultAmountDoesNotMatchParams","type":"error"},{"inputs":[],"name":"VaultAmountLocked","type":"error"},{"inputs":[],"name":"VaultCallerNotAdmin","type":"error"},{"inputs":[],"name":"VaultCallerNotKeeper","type":"error"},{"inputs":[],"name":"VaultCallerNotSettings","type":"error"},{"inputs":[],"name":"VaultCallerNotSponsor","type":"error"},{"inputs":[],"name":"VaultCannotComputeSharesWithoutPrincipal","type":"error"},{"inputs":[],"name":"VaultCannotDeposit0","type":"error"},{"inputs":[],"name":"VaultCannotDepositWhenClaimerInDebt","type":"error"},{"inputs":[],"name":"VaultCannotDepositWhenYieldNegative","type":"error"},{"inputs":[],"name":"VaultCannotSponsor0","type":"error"},{"inputs":[],"name":"VaultCannotTransferAdminRightsToSelf","type":"error"},{"inputs":[],"name":"VaultCannotWithdrawMoreThanAvailable","type":"error"},{"inputs":[],"name":"VaultCannotWithdrawWhenYieldNegative","type":"error"},{"inputs":[],"name":"VaultClaimPercentageCannotBe0","type":"error"},{"inputs":[],"name":"VaultClaimerCannotBe0","type":"error"},{"inputs":[],"name":"VaultClaimsDontAddUp","type":"error"},{"inputs":[],"name":"VaultDepositLocked","type":"error"},{"inputs":[],"name":"VaultDepositNameTooShort","type":"error"},{"inputs":[],"name":"VaultDestinationCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultInvalidInvestpct","type":"error"},{"inputs":[],"name":"VaultInvalidLockPeriod","type":"error"},{"inputs":[],"name":"VaultInvalidLossTolerance","type":"error"},{"inputs":[],"name":"VaultInvalidMinLockPeriod","type":"error"},{"inputs":[],"name":"VaultInvalidPerformanceFee","type":"error"},{"inputs":[],"name":"VaultInvalidVault","type":"error"},{"inputs":[],"name":"VaultMustUseForceWithdrawToAcceptLosses","type":"error"},{"inputs":[],"name":"VaultNoPerformanceFee","type":"error"},{"inputs":[],"name":"VaultNoYieldToClaim","type":"error"},{"inputs":[],"name":"VaultNotAllowed","type":"error"},{"inputs":[],"name":"VaultNotDeposit","type":"error"},{"inputs":[],"name":"VaultNotEnoughFunds","type":"error"},{"inputs":[],"name":"VaultNotEnoughToRebalance","type":"error"},{"inputs":[],"name":"VaultNotOwnerOfDeposit","type":"error"},{"inputs":[],"name":"VaultNotSponsor","type":"error"},{"inputs":[],"name":"VaultNothingToDo","type":"error"},{"inputs":[],"name":"VaultSenderNotOwnerOfGroupId","type":"error"},{"inputs":[],"name":"VaultStrategyHasInvestedFunds","type":"error"},{"inputs":[],"name":"VaultStrategyNotSet","type":"error"},{"inputs":[],"name":"VaultTreasuryCannotBe0Address","type":"error"},{"inputs":[],"name":"VaultUnderlyingCannotBe0Address","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"int128","name":"tokenI","type":"int128"},{"indexed":false,"internalType":"int128","name":"underlyingI","type":"int128"}],"name":"CurveSwapPoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"CurveSwapPoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"groupId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"address","name":"claimerId","type":"address"},{"indexed":false,"internalType":"uint64","name":"lockedUntil","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"DepositMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bool","name":"burned","type":"bool"}],"name":"DepositWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Disinvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"ExitPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"ExitUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeeWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"InvestPctUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Invested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"pct","type":"uint16"}],"name":"LossTolerancePctUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"pct","type":"uint16"}],"name":"PerfFeePctUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"name":"Sponsored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"treasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bool","name":"burned","type":"bool"}],"name":"Unsponsored","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"claimerId","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnedShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"perfFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalUnderlying","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DEPOSIT_LOCK_DURATION","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SPONSOR_LOCK_DURATION","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_SPONSOR_LOCK_DURATION","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTINGS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHARES_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SPONSOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accumulatedPerfFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"int128","name":"tokenI","type":"int128"},{"internalType":"int128","name":"underlyingI","type":"int128"}],"internalType":"struct CurveSwapper.SwapPoolParam","name":"_param","type":"tuple"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"claimYield","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimer","outputs":[{"internalType":"uint256","name":"totalPrincipal","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint64","name":"lockDuration","type":"uint64"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint16","name":"pct","type":"uint16"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IVault.ClaimParams[]","name":"claims","type":"tuple[]"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"slippage","type":"uint256"}],"internalType":"struct IVault.DepositParams","name":"_params","type":"tuple"}],"name":"deposit","outputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_groupId","type":"uint256"},{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint64","name":"lockDuration","type":"uint64"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint16","name":"pct","type":"uint16"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IVault.ClaimParams[]","name":"claims","type":"tuple[]"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"slippage","type":"uint256"}],"internalType":"struct IVault.DepositParams","name":"_params","type":"tuple"}],"name":"depositForGroupId","outputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositGroupIdOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deposits","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"claimerId","type":"address"},{"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exitPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitUnpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"forceWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnderlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"investPct","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"investState","outputs":[{"internalType":"uint256","name":"maxInvestableAmount","type":"uint256"},{"internalType":"uint256","name":"alreadyInvested","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lossTolerancePct","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minLockPeriod","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"partialUnsponsor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"partialWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perfFeePct","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"claimerId","type":"address"}],"name":"principalOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputToken","type":"address"}],"name":"removePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_investPct","type":"uint16"}],"name":"setInvestPct","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"pct","type":"uint16"}],"name":"setLossTolerancePct","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_perfFeePct","type":"uint16"}],"name":"setPerfFeePct","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"setStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"claimerId","type":"address"}],"name":"sharesOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"sponsor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"swappers","outputs":[{"internalType":"contract ICurve","name":"pool","type":"address"},{"internalType":"uint8","name":"tokenDecimals","type":"uint8"},{"internalType":"uint8","name":"underlyingDecimals","type":"uint8"},{"internalType":"int128","name":"tokenI","type":"int128"},{"internalType":"int128","name":"underlyingI","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSponsored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnderlyingMinusSponsored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"transferAdminRights","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"unsponsor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateInvested","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"yieldFor","outputs":[{"internalType":"uint256","name":"claimableYield","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"perfFee","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c06040523480156200001157600080fd5b50604051620065c3380380620065c3833981016040819052620000349162000d80565b60016002556003805461ffff191690556200005f61ffff871662000390602090811b6200241617901c565b6200007d5760405163870e44a360e01b815260040160405180910390fd5b620000978361ffff166200039060201b620024161760201c565b620000b55760405163d25b1e4b60e01b815260040160405180910390fd5b620000cf8261ffff166200039060201b620024161760201c565b620000ed57604051631d05623160e01b815260040160405180910390fd5b6001600160a01b0388166200011557604051631fc8ba7960e31b815260040160405180910390fd5b6001600160a01b0385166200013d576040516335af31e960e11b815260040160405180910390fd5b6001600160a01b03841662000165576040516330c9889360e01b815260040160405180910390fd5b6001600160401b038716158062000187575062dd7c006001600160401b038816115b15620001a657604051636df7cef760e11b815260040160405180910390fd5b620001b360008562000398565b620001df7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab8562000398565b6200020b7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf6030958562000398565b620002377f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a8562000398565b6003805462010000600160c01b031916600160b01b61ffff8981169190910262010000600160b01b03191691909117620100006001600160a01b038c811682029290921793849055600d80546001600160401b038d166080528a84166001600160b01b031990911617600160a01b89861602179055600f805461ffff1916938716939093179092556040805163313ce56760e01b8152905192909304169163313ce5679160048083019260209291908290030181865afa15801562000300573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000326919062000f38565b6200033390600a62001070565b6200034090600a62001081565b60a0526200034e8162000407565b6040516001600160a01b038616907f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a25050505050505050620011bb565b612710101590565b620003a482826200045a565b620004035760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45b5050565b805160005b818110156200045557620004428382815181106200042e576200042e620010a3565b60200260200101516200048760201b60201c565b6200044d81620010b9565b90506200040c565b505050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b80516001600160a01b039081166000908152602081905260409020541615620004f75760405162461bcd60e51b815260206004820152601d60248201527f746f6b656e20616c7265616479206861732061207377617020706f6f6c00000060448201526064015b60405180910390fd5b620005106003546201000090046001600160a01b031690565b6001600160a01b031681602001516001600160a01b031663c661065783606001516001600160801b03166040518263ffffffff1660e01b81526004016200055991815260200190565b602060405180830381865afa15801562000577573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200059d9190620010d7565b6001600160a01b0316146200060a5760405162461bcd60e51b815260206004820152602c60248201527f5f756e6465726c79696e674920646f6573206e6f74206d6174636820756e646560448201526b39363cb4b733903a37b5b2b760a11b6064820152608401620004ee565b600081600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200064f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000675919062000f38565b60ff1690506000620006956003546201000090046001600160a01b031690565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620006d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006f9919062000f38565b6040805160a0810182526020808701516001600160a01b03908116835260ff8781168385019081529581168486018181528a870151600f90810b60608089019182528d015190910b608088019081528c5186166000908152968790529790952095518654985191518416600160a81b0260ff60a81b1992909416600160a01b026001600160a81b031990991694169390931796909617919091161782555191516001600160801b03908116600160801b029216919091176001909101559050620007e0620007d56003546201000090046001600160a01b031690565b602085015162000865565b82516020840151620007f3919062000865565b82602001516001600160a01b031683600001516001600160a01b03167f1576b5fcfa863788d9ef6558cab7aa68d4b48c44eb7bacaf1a8ac6d6b9d26f948560400151866060015160405162000858929190600f92830b8152910b602082015260400190565b60405180910390a3505050565b604051636eb1769f60e11b81523060048201526001600160a01b0382811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015620008b6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008dc9190620010f7565b90508062000455576200045582600019856001600160a01b03166200090b60201b6200241e179092919060201c565b801580620009895750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801562000961573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620009879190620010f7565b155b620009fd5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401620004ee565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526200045591859162000a5516565b600062000ab1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031662000b3360201b6200259b179092919060201c565b80519091501562000455578080602001905181019062000ad2919062001111565b620004555760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401620004ee565b606062000b44848460008562000b4e565b90505b9392505050565b60608247101562000bb15760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401620004ee565b6001600160a01b0385163b62000c0a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620004ee565b600080866001600160a01b0316858760405162000c28919062001168565b60006040518083038185875af1925050503d806000811462000c67576040519150601f19603f3d011682016040523d82523d6000602084013e62000c6c565b606091505b50909250905062000c7f82828662000c8a565b979650505050505050565b6060831562000c9b57508162000b47565b82511562000cac5782518084602001fd5b8160405162461bcd60e51b8152600401620004ee919062001186565b6001600160a01b038116811462000cde57600080fd5b50565b805161ffff8116811462000cf457600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b038111828210171562000d345762000d3462000cf9565b60405290565b604051601f8201601f191681016001600160401b038111828210171562000d655762000d6562000cf9565b604052919050565b8051600f81900b811462000cf457600080fd5b600080600080600080600080610100898b03121562000d9e57600080fd5b885162000dab8162000cc8565b60208a01519098506001600160401b03808216821462000dca57600080fd5b81985062000ddb60408c0162000ce1565b975060608b0151915062000def8262000cc8565b60808b015191965062000e028262000cc8565b81955062000e1360a08c0162000ce1565b945062000e2360c08c0162000ce1565b935060e08b015191508082111562000e3a57600080fd5b818b0191508b601f83011262000e4f57600080fd5b81518181111562000e645762000e6462000cf9565b62000e7560208260051b0162000d3a565b91508181835260208301925060208260071b85010191508d82111562000e9a57600080fd5b6020840193505b8184101562000f24576080848f03121562000ebb57600080fd5b62000ec562000d0f565b845162000ed28162000cc8565b8152602085015162000ee48162000cc8565b602082015262000ef76040860162000d6d565b604082015262000f0a6060860162000d6d565b606082015283526080939093019260209092019162000ea1565b809450505050509295985092959890939650565b60006020828403121562000f4b57600080fd5b815160ff8116811462000b4757600080fd5b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000fb457816000190482111562000f985762000f9862000f5d565b8085161562000fa657918102915b93841c939080029062000f78565b509250929050565b60008262000fcd5750600162000481565b8162000fdc5750600062000481565b816001811462000ff55760028114620010005762001020565b600191505062000481565b60ff84111562001014576200101462000f5d565b50506001821b62000481565b5060208310610133831016604e8410600b841016171562001045575081810a62000481565b62001051838362000f73565b806000190482111562001068576200106862000f5d565b029392505050565b600062000b4760ff84168362000fbc565b60008160001904831182151516156200109e576200109e62000f5d565b500290565b634e487b7160e01b600052603260045260246000fd5b6000600019821415620010d057620010d062000f5d565b5060010190565b600060208284031215620010ea57600080fd5b815162000b478162000cc8565b6000602082840312156200110a57600080fd5b5051919050565b6000602082840312156200112457600080fd5b8151801515811462000b4757600080fd5b60005b838110156200115257818101518382015260200162001138565b8381111562001162576000848401525b50505050565b600082516200117c81846020870162001135565b9190910192915050565b6020815260008251806020840152620011a781604085016020870162001135565b601f01601f19169190910160400192915050565b60805160a0516153d4620011ef600039600081816110d401526111bf0152600081816105dd01526131e701526153d46000f3fe608060405234801561001057600080fd5b50600436106103af5760003560e01c80638456cb59116101f4578063c0ddb6921161011a578063df6f9ba7116100ad578063f5eb42dc1161007c578063f5eb42dc14610951578063f62898871461097d578063f78797c0146109a4578063fd6e9217146109cd57600080fd5b8063df6f9ba71461090f578063e4020fdf14610922578063eb0ebfbc14610935578063f0f442601461093e57600080fd5b8063cd0d9ba8116100e9578063cd0d9ba8146108c1578063cda2695a146108d4578063d547741f146108e7578063d9db2b77146108fa57600080fd5b8063c0ddb69214610877578063c2d794441461087f578063c70920bc146108a6578063cd00dd41146108ae57600080fd5b8063a217fddf11610192578063b5106add11610161578063b5106add14610816578063b59a40cb14610829578063bba97d3314610849578063bd2e3ecd1461061857600080fd5b8063a217fddf14610779578063a8c62e7614610781578063abaa9d7014610794578063b02c43d0146107a457600080fd5b80639816f473116101ce5780639816f47314610732578063999927df14610749578063a164c25d1461075c578063a1ac20eb1461076657600080fd5b80638456cb591461065b5780638cad7fbe1461066357806391d14854146106f957600080fd5b80634e813ac4116102d957806361e20a1c1161027757806376490b561161024657806376490b56146106185780637e2cba6f146106225780637e4b8da0146106355780638293744b1461064857600080fd5b806361e20a1c146105755780636e7007e61461059e5780636f307dc3146105bf57806373ae54b5146105d857600080fd5b80635c975abb116102b35780635c975abb1461052f5780635cb0d4061461053a5780635e8486551461054257806361d027b31461054a57600080fd5b80634e813ac41461050c5780634fc78d6814610514578063563233f61461051c57600080fd5b8063364bc15a116103515780633ba52fc6116103205780633ba52fc6146104e05780633f383236146104e85780633f4ba83a146104fb5780634b1533b21461050357600080fd5b8063364bc15a1461048a57806336568abe146104b15780633a98ef39146104c45780633b7d0946146104cd57600080fd5b806319d3ee3d1161038d57806319d3ee3d14610435578063248a9ca31461043e5780632f2ff15d1461046257806333a100ca1461047757600080fd5b806301ffc9a7146103b45780631325d5f1146103dc57806313f6686d146103f9575b600080fd5b6103c76103c23660046149a8565b6109e2565b60405190151581526020015b60405180910390f35b6103eb670de0b6b3a764000081565b6040519081526020016103d3565b6104206104073660046149e7565b600b602052600090815260409020805460019091015482565b604080519283526020830191909152016103d3565b6103eb600e5481565b6103eb61044c366004614a04565b6000908152600160208190526040909120015490565b610475610470366004614a1d565b610a5a565b005b6104756104853660046149e7565b610a85565b6103eb7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b6104756104bf366004614a1d565b610cb4565b6103eb60055481565b6104756104db3660046149e7565b610d45565b610475610da0565b6104756104f6366004614ad4565b610e8d565b610475610ee5565b6103eb600c5481565b610420610f3e565b610475610ffb565b61047561052a366004614b67565b6112ba565b60035460ff166103c7565b6104756113ba565b6104756113cd565b600d5461055d906001600160a01b031681565b6040516001600160a01b0390911681526020016103d3565b6103eb6105833660046149e7565b6001600160a01b03166000908152600b602052604090205490565b600f546105ac9061ffff1681565b60405161ffff90911681526020016103d3565b60035461055d906201000090046001600160a01b031681565b6105ff7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016103d3565b6105ff62dd7c0081565b610475610630366004614b67565b6113e0565b610475610643366004614bce565b6114b3565b610475610656366004614c51565b611551565b610475611630565b6106ba6106713660046149e7565b600060208190529081526040902080546001909101546001600160a01b0382169160ff600160a01b8204811692600160a81b9092041690600f81810b91600160801b9004900b85565b604080516001600160a01b03909616865260ff94851660208701529390921692840192909252600f91820b6060840152900b608082015260a0016103d3565b6103c7610707366004614a1d565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6003546201000090046001600160a01b031661055d565b6104756107573660046149e7565b611687565b6105ff6212750081565b610475610774366004614b67565b61185a565b6103eb600081565b60065461055d906001600160a01b031681565b600354610100900460ff166103c7565b6107e66107b2366004614a04565b600960205260009081526040902080546001820154600283015460039093015491926001600160a01b039182169291169084565b6040516103d394939291909384526001600160a01b03928316602085015291166040830152606082015260800190565b6104756108243660046149e7565b61195c565b61083c610837366004614cbe565b611b40565b6040516103d39190614cfb565b61085c6108573660046149e7565b611be5565b604080519384526020840192909252908201526060016103d3565b6103eb611ca4565b6103eb7f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a81565b6103eb611ce7565b6104756108bc366004614bce565b611e63565b61083c6108cf366004614d3f565b611ef5565b6104756108e2366004614d86565b611faf565b6104756108f5366004614a1d565b6121f5565b600d546105ac90600160a01b900461ffff1681565b61047561091d366004614c51565b61221b565b610475610930366004614c51565b6122ad565b6103eb60045481565b61047561094c3660046149e7565b61233d565b6103eb61095f3660046149e7565b6001600160a01b03166000908152600b602052604090206001015490565b6103eb7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309581565b61055d6109b2366004614a04565b6008602052600090815260409020546001600160a01b031681565b6003546105ac90600160b01b900461ffff1681565b60006001600160e01b031982167f21d982ed000000000000000000000000000000000000000000000000000000001480610a4557506001600160e01b031982167f0fae047800000000000000000000000000000000000000000000000000000000145b80610a545750610a54826125b4565b92915050565b60008281526001602081905260409091200154610a768161261b565b610a808383612625565b505050565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff16610ad457604051631e15456f60e11b815260040160405180910390fd5b6001600160a01b038116610afb57604051634f08b3f360e11b815260040160405180910390fd5b306001600160a01b0316816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b679190614dc1565b6001600160a01b031614610ba7576040517f0251bab500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b031615801590610c335750600660009054906101000a90046001600160a01b03166001600160a01b0316635be9b2d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c339190614dde565b15610c6a576040517fd28a9fdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383169081179091556040517f4da9c22c924692646a21cf1f423781ae3285198dc22e8a6912835d3272b90b3c90600090a250565b6001600160a01b0381163314610d375760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b610d4182826126ac565b5050565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610d9457604051631bacfc6960e01b815260040160405180910390fd5b610d9d8161272f565b50565b3360009081527f10c15aae13326e9b33c92ffbb94efb7492c500f0cca324caca9bd1aca194ed18602052604090205460ff16610def57604051637660f80560e11b815260040160405180910390fd5b600e5480610e29576040517f0a33934000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600e55610e3781612803565b6040518181527fb7eeacba6b133788365610e83d3f130d07b6ef6e78877961f25b3f61fcba07529060200160405180910390a1600d54600354610d9d916001600160a01b03620100009092048216911683612a02565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610edc57604051631bacfc6960e01b815260040160405180910390fd5b610d9d81612a4b565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610f3457604051631bacfc6960e01b815260040160405180910390fd5b610f3c612e62565b565b60065460009081906001600160a01b0316610f5c5750600091829150565b600354610f7c90600160b01b900461ffff16610f76611ce7565b90612eb4565b9150600660009054906101000a90046001600160a01b03166001600160a01b0316634ad0b6846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff59190614e00565b90509091565b3360009081527f10c15aae13326e9b33c92ffbb94efb7492c500f0cca324caca9bd1aca194ed18602052604090205460ff1661104a57604051637660f80560e11b815260040160405180910390fd5b6006546001600160a01b031661107357604051634f08b3f360e11b815260040160405180910390fd5b60008061107e610f3e565b91509150808214156110bc576040517ff096593000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818111156111af5760006110d08383614e2f565b90507f000000000000000000000000000000000000000000000000000000000000000081101561111357604051630e33bdb760e11b815260040160405180910390fd5b6006546040516319d1885d60e31b8152600481018390526001600160a01b039091169063ce8c42e890602401600060405180830381600087803b15801561115957600080fd5b505af115801561116d573d6000803e3d6000fd5b505050507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd29816040516111a291815260200190565b60405180910390a1505050565b60006111bb8284614e2f565b90507f00000000000000000000000000000000000000000000000000000000000000008110156111fe57604051630e33bdb760e11b815260040160405180910390fd5b600654600354611221916001600160a01b03620100009092048216911683612a02565b600660009054906101000a90046001600160a01b03166001600160a01b031663e8b5e51f6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561127157600080fd5b505af1158015611285573d6000803e3d6000fd5b505050507fac9f7997c30b6a3cc9c74953898b5de154359339c7ec0d6d70ceb98e55db1a4b816040516111a291815260200190565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661130957604051631e15456f60e11b815260040160405180910390fd5b61ffff81166127101015611349576040517fd25b1e4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff16600160a01b61ffff8416908102919091179091556040519081527f16de0efbdf16c568a60b65978ed774bba4e0c179de55ab8f639527e41fd0c861906020015b60405180910390a150565b60006113c58161261b565b610d9d612ed1565b60006113d88161261b565b610d9d612f10565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661142f57604051631e15456f60e11b815260040160405180910390fd5b61ffff8116612710101561146f576040517f1d05623100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f805461ffff191661ffff83169081179091556040519081527f5b9aa28fc994f782b33ebebd9947795b5bc59a54203466433eac5f8d9e819400906020016113af565b6002805414156115055760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611511612f4a565b6001600160a01b0385166115385760405163f95106ab60e01b815260040160405180910390fd5b6115458585858585612fa2565b50506001600255505050565b6002805414156115a35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280556115af612f4a565b6001600160a01b0383166115d65760405163f95106ab60e01b815260040160405180910390fd5b6115de611ca4565b600c541115611619576040517f0bdd660c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611626838383600061304b565b5050600160025550565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff1661167f57604051631bacfc6960e01b815260040160405180910390fd5b610f3c61310f565b6002805414156116d95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280556116e5612f4a565b6001600160a01b03811661170c5760405163f95106ab60e01b815260040160405180910390fd5b600080600061171a33611be5565b925092509250826000141561175b576040517f58367fe100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611765611ca4565b90506000600554905082600e60008282546117809190614e46565b9091555061178f905085612803565b6003546117ac906201000090046001600160a01b03168787612a02565b336000908152600b6020526040812060010180548692906117ce908490614e2f565b9250508190555083600560008282546117e79190614e2f565b90915550506040805133815260208101879052908101859052606081018490526080810183905260a081018290526001600160a01b038716907f4344accc2a634cf29195bba6fa3d939e3415ecd1ca5c57b322bcf81f920979c89060c00160405180910390a25050600160025550505050565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff166118a957604051631e15456f60e11b815260040160405180910390fd5b61ffff811661271010156118e9576040517f870e44a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405161ffff821681527f40775ca73a6e168d23747bc0200694cf7c99ad2db624d04c095cb59899011a569060200160405180910390a16003805461ffff909216600160b01b027fffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffff909216919091179055565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff166119ab57604051631bacfc6960e01b815260040160405180910390fd5b6001600160a01b0381166119eb576040517f30c9889300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116331415611a2e576040517f25499f7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a39600082612625565b611a637ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab82612625565b611a8d7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309582612625565b611ab77f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a82612625565b611ac26000336126ac565b611aec7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab336126ac565b611b167ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf603095336126ac565b610d9d7f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a336126ac565b6060600280541415611b945760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611ba061314c565b600754611bae816001614e46565b600755600081815260086020526040902080546001600160a01b03191633179055611bd9818461319f565b60016002559392505050565b6001600160a01b0381166000908152600b6020526040812080546001909101548291829182611c12611ca4565b90506000611c238360055484613392565b9050838111611c4057600080600096509650965050505050611c9d565b6000611c4c8583614e2f565b9050611c5b81600554856133ca565b96506000611c6c8860055486613392565b600d54909150611c88908290600160a01b900461ffff16612eb4565b9650611c948782614e2f565b98505050505050505b9193909250565b600080611caf611ce7565b90506000600e54600454611cc39190614e46565b905081811115611cd65760009250505090565b611ce08183614e2f565b9250505090565b6006546000906001600160a01b031615611df257600660009054906101000a90046001600160a01b03166001600160a01b0316634ad0b6846040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d729190614e00565b6003546040516370a0823160e01b8152306004820152620100009091046001600160a01b0316906370a0823190602401602060405180830381865afa158015611dbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de39190614e00565b611ded9190614e46565b905090565b6003546040516370a0823160e01b8152306004820152620100009091046001600160a01b0316906370a0823190602401602060405180830381865afa158015611e3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ded9190614e00565b600280541415611eb55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611ec1612f4a565b6001600160a01b038516611ee85760405163f95106ab60e01b815260040160405180910390fd5b6115458585858585613439565b6060600280541415611f495760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611f5561314c565b6000838152600860205260409020546001600160a01b03163314611fa5576040517f9049b68600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bd9838361319f565b6002805414156120015760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280553360009081527f27365024d84ecf053ef4d13025b66be161b03e90a7fc7ff54f74d5533a9f857e602052604090205460ff1661206d576040517f9aff7af000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207561314c565b826120ac576040517ff712805800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b621275008210806120bf575062dd7c0082115b156120dd57604051636235acd960e01b815260040160405180910390fd5b60006120e94284614e46565b90506120f9600a80546001019055565b6000612104600a5490565b90506121113387876134c7565b600061211e8787866135ff565b604080516080810182528281523360208083019182526000838501818152606085018a815289835260099093529481209351845591516001840180546001600160a01b03199081166001600160a01b0393841617909155945160028501805490961691161790935591516003909101556004805492935083929091906121a5908490614e46565b90915550506040805182815260208101859052339184917f7ca3497675c6d3ec00b1edc13b8e0b06169ee96a663ab25a74af7da53378f09e910160405180910390a3505060016002555050505050565b600082815260016020819052604090912001546122118161261b565b610a8083836126ac565b60028054141561226d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055612279612f4a565b6001600160a01b0383166122a05760405163f95106ab60e01b815260040160405180910390fd5b611626838383600161304b565b6002805414156122ff5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b6002805561230b612f4a565b6001600160a01b0383166123325760405163f95106ab60e01b815260040160405180910390fd5b6116268383836137df565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661238c57604051631e15456f60e11b815260040160405180910390fd5b6001600160a01b0381166123cc576040517f6b5e63d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0383169081179091556040517f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a250565b612710101590565b8015806124985750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612472573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124969190614e00565b155b61250a5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610d2e565b6040516001600160a01b038316602482015260448101829052610a809084907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915261385a565b60606125aa848460008561393f565b90505b9392505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610a5457507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610a54565b610d9d8133613a7c565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16610d415760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff1615610d415760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b03818116600090815260208190526040902054166127965760405162461bcd60e51b815260206004820152601360248201527f706f6f6c20646f6573206e6f74206578697374000000000000000000000000006044820152606401610d2e565b6001600160a01b03811660008181526020819052604080822080547fffffffffffffffffffff00000000000000000000000000000000000000000000168155600101829055517f94be23730c4398d20227f4e457cf92307b13e5956f2a958e2e368ad44e2f826b9190a250565b6003546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a0823190602401602060405180830381865afa158015612852573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128769190614e00565b9050808211612883575050565b600660009054906101000a90046001600160a01b03166001600160a01b0316639af2e6356040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fa9190614dde565b61291757604051635f842b9960e11b815260040160405180910390fd5b60035460009061294b9061293890600160b01b900461ffff16612710614e5e565b84612941611ce7565b610f769190614e2f565b905060008261295a8386614e46565b6129649190614e2f565b6006546040516319d1885d60e31b8152600481018390529192506001600160a01b03169063ce8c42e890602401600060405180830381600087803b1580156129ab57600080fd5b505af11580156129bf573d6000803e3d6000fd5b505050507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd29816040516129f491815260200190565b60405180910390a150505050565b6040516001600160a01b038316602482015260448101829052610a809084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161254f565b80516001600160a01b039081166000908152602081905260409020541615612ab55760405162461bcd60e51b815260206004820152601d60248201527f746f6b656e20616c7265616479206861732061207377617020706f6f6c0000006044820152606401610d2e565b6003546201000090046001600160a01b03166001600160a01b031681602001516001600160a01b031663c661065783606001516fffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612b1891815260200190565b602060405180830381865afa158015612b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b599190614dc1565b6001600160a01b031614612bd55760405162461bcd60e51b815260206004820152602c60248201527f5f756e6465726c79696e674920646f6573206e6f74206d6174636820756e646560448201527f726c79696e6720746f6b656e00000000000000000000000000000000000000006064820152608401610d2e565b600081600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3d9190614e81565b60ff1690506000612c5d6003546001600160a01b03620100009091041690565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cbe9190614e81565b6040805160a0810182526020808701516001600160a01b03908116835260ff8781168385019081529581168486018181528a870151600f90810b60608089019182528d015190910b608088019081528c5186166000908152968790529790952095518654985191518416600160a81b027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff92909416600160a01b027fffffffffffffffffffffff00000000000000000000000000000000000000000090991694169390931796909617919091161782555191516fffffffffffffffffffffffffffffffff908116600160801b029216919091176001909101559050612ddf612dd56003546001600160a01b03620100009091041690565b8460200151613afc565b612df183600001518460200151613afc565b82602001516001600160a01b031683600001516001600160a01b03167f1576b5fcfa863788d9ef6558cab7aa68d4b48c44eb7bacaf1a8ac6d6b9d26f9485604001518660600151604051612e55929190600f92830b8152910b602082015260400190565b60405180910390a3505050565b612e6a613b8d565b6003805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000612710612ec761ffff841685614ea4565b6125ad9190614ec3565b612ed9612f4a565b6003805461ff0019166101001790557f92493a2527e1a0e4214d0f98b1bedbdc2b53ef81ee4578756108fef0deff09ee612e973390565b612f18613bdf565b6003805461ff00191690557fc47f9c912614e04aeb80c7e6cdc73c0f4368cde4bf42df3b26ad96b898913c7d33612e97565b600354610100900460ff1615610f3c5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a20457869745061757365640000000000000000000000006044820152606401610d2e565b6005546000612faf611ca4565b9050600085815b8181101561301957612ffd898983818110612fd357612fd3614ee5565b9050602002013586868d60008c8c88818110612ff157612ff1614ee5565b90506020020135613c36565b6130079084614e46565b925061301281614efb565b9050612fb6565b5061302382612803565b600354613040906201000090046001600160a01b03168a84612a02565b505050505050505050565b6005546000613058611ca4565b9050600084815b818110156130de576000600960008a8a8581811061307f5761307f614ee5565b9050602002013581526020019081526020016000206000015490506130c08989848181106130af576130af614ee5565b9050602002013587878d8b86613c36565b6130ca9085614e46565b935050806130d790614efb565b905061305f565b506130e882612803565b600354613105906201000090046001600160a01b03168984612a02565b5050505050505050565b61311761314c565b6003805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612e973390565b60035460ff1615610f3c5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610d2e565b606060408201356131dc576040517fab59997c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166132176040840160208501614f16565b67ffffffffffffffff161080613248575062dd7c0061323c6040840160208501614f16565b67ffffffffffffffff16115b1561326657604051636235acd960e01b815260040160405180910390fd5b60036132756080840184614f40565b905010156132af576040517f3867858900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006132bc600c54613f98565b905060006132c8611ca4565b905080821115613304576040517f08063cab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61331f3361331560208701876149e7565b86604001356134c7565b600061334061333160208701876149e7565b86604001358760a001356135ff565b90506000426133556040880160208901614f16565b61335f9190614f87565b905061338783838361337460608b018b614fb3565b61338160808d018d614f40565b8e613fb7565b979650505050505050565b600083158061339f575082155b806133a8575081155b156133b5575060006125ad565b826133c08584614ea4565b6125aa9190614ec3565b6000836133d9575060006125ad565b826133f7576133f0670de0b6b3a764000085614ea4565b90506125ad565b8161342e576040517ff91b6e7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816133c08486614ea4565b600083815b818160ff1610156134b357600087878360ff1681811061346057613460614ee5565b905060200201359050600086868460ff1681811061348057613480614ee5565b9050602002013590506134948a83836141f9565b61349e8186614e46565b94505050806134ac90614ffd565b905061343e565b506134be87836143d6565b50505050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561350e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135329190614e00565b90506135496001600160a01b03841685308561443c565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015613590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135b49190614e00565b90506135c08383614e46565b81146135f8576040517f2a25a38b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b60008061361b6003546001600160a01b03620100009091041690565b9050806001600160a01b0316856001600160a01b0316141561364057839150506125ad565b6001600160a01b03808616600090815260208190526040902080549091166136aa5760405162461bcd60e51b815260206004820152601660248201527f6e6f6e2d6578697374696e67207377617020706f6f6c000000000000000000006044820152606401610d2e565b80546000906136cf90879060ff600160a01b8204811691600160a81b90041688614493565b825460018401546040517fa6417ed6000000000000000000000000000000000000000000000000000000008152600f82810b6004830152600160801b90920490910b602482015260448101899052606481018390529192506001600160a01b03169063a6417ed6906084016020604051808303816000875af1158015613759573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061377d9190614e00565b9350826001600160a01b0316876001600160a01b03167ffa2dda1cc1b86e41239702756b13effbc1a092b5c57e3ad320fbe4f3b13fe23588876040516137cd929190918252602082015260400190565b60405180910390a35050509392505050565b600081815b818160ff16101561384f57600085858360ff1681811061380657613806614ee5565b602090810292909201356000818152600990935260409092205491925061383090508883836141f9565b61383a8186614e46565b945050508061384890614ffd565b90506137e4565b506135f885836143d6565b60006138af826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661259b9092919063ffffffff16565b805190915015610a8057808060200190518101906138cd9190614dde565b610a805760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d2e565b6060824710156139b75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610d2e565b6001600160a01b0385163b613a0e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d2e565b600080866001600160a01b03168587604051613a2a9190615049565b60006040518083038185875af1925050503d8060008114613a67576040519150601f19603f3d011682016040523d82523d6000602084013e613a6c565b606091505b50915091506133878282866144de565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16610d4157613aba816001600160a01b03166014614517565b613ac5836020614517565b604051602001613ad6929190615065565b60408051601f198184030181529082905262461bcd60e51b8252610d2e91600401615112565b604051636eb1769f60e11b81523060048201526001600160a01b0382811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015613b4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b709190614e00565b905080610a8057610a806001600160a01b0384168360001961241e565b60035460ff16610f3c5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610d2e565b600354610100900460ff16610f3c5760405162461bcd60e51b815260206004820152601860248201527f5061757361626c653a206e6f74204578697450617573656400000000000000006044820152606401610d2e565b6000868152600960205260408120600101546001600160a01b03163314613c89576040517fff40736100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008781526009602090815260408083208151608081018352815481526001808301546001600160a01b039081168387015260028401541682850181905260039093015460608301908152928652600b85529483902083518085019094528054845290940154928201929092529051421015613d31576040517f0f6268c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516001600160a01b0316613d75576040517fde8b700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151841115613d9757604051635bcd1de160e11b815260040160405180910390fd5b6000613da4858a8a6133ca565b82516020840151919250600091613dbb9088614ea4565b613dc59190614ec3565b905086158015613dd457508082115b15613e0b576040517f7df2e7ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81878015613e1857508183115b15613e205750805b6040808601516001600160a01b03166000908152600b6020529081206001018054839290613e4f908490614e2f565b90915550506040808601516001600160a01b03166000908152600b6020529081208054899290613e80908490614e2f565b925050819055508060056000828254613e999190614e2f565b9250508190555086600c6000828254613eb29190614e2f565b9091555050845187148015613efb5760008d81526009602052604081208181556001810180546001600160a01b0319908116909155600282018054909116905560030155613f1f565b60008d815260096020526040812080548a9290613f19908490614e2f565b90915550505b6000613f2c838e8e613392565b90508a6001600160a01b03168e7f14e79002ce0df3ba4381425c583d4c9984c7e2910d3e7e088377db997773153e858486604051613f7f9392919092835260208301919091521515604082015260600190565b60405180910390a39d9c50505050505050505050505050565b600f54600090613fad90839061ffff16612eb4565b610a549083614e2f565b6040805160a0810182526005548152602081018a90526000918101829052606081810183905260808201879052918667ffffffffffffffff811115613ffe57613ffe614a4d565b604051908082528060200260200182016040528015614027578160200160208202803683370190505b50905060005b82608001518110156141a157600089898381811061404d5761404d614ee5565b905060200281019061405f9190615125565b6140689061513b565b805190915061ffff166140a7576040517f4f287e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516001600160a01b03166140eb576040517fd444696b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600185608001516140fe9190614e2f565b8314614116578151614111908e90612eb4565b614125565b6060850151614125908e614e2f565b905061413f87828e8589600001518a602001518f8f6146f8565b84848151811061415157614151614ee5565b6020908102919091010152815160408601805161416f9083906151fb565b61ffff1690525060608501805182919061418a908390614e46565b90525061419a9150829050614efb565b905061402d565b506141b5826040015161ffff166127101490565b6141eb576040517fd7b03e8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9a9950505050505050505050565b60008281526009602090815260409182902082516080810184528154815260018201546001600160a01b0390811693820184905260028301541693810193909352600301546060830152331461427b576040517f5040aaa300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42816060015111156142b9576040517f4cf1715e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408101516001600160a01b0316156142fe576040517f6f15ccf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805182111561432057604051635bcd1de160e11b815260040160405180910390fd5b80516040805184815291841460208301819052916001600160a01b0387169186917f8bb6f71206f2ec5e5a42539a0d677bb7e48844b6cefdad5d7139fd68568d7299910160405180910390a38061439b576000848152600960205260408120805485929061438f908490614e2f565b90915550505050505050565b50505060009081526009602052604081208181556001810180546001600160a01b031990811690915560028201805490911690556003015550565b6143de611ce7565b8111156143fe57604051635f842b9960e11b815260040160405180910390fd5b80600460008282546144109190614e2f565b9091555061441f905081612803565b600354610d41906201000090046001600160a01b03168383612a02565b6040516001600160a01b038085166024830152831660448201526064810182905261448d9085907f23b872dd000000000000000000000000000000000000000000000000000000009060840161254f565b50505050565b60006144a084600a6152fc565b6144ac90612710614ea4565b6144b784600a6152fc565b6144c18488614ea4565b6144cb9190614ea4565b6144d59190614ec3565b95945050505050565b606083156144ed5750816125ad565b8251156144fd5782518084602001fd5b8160405162461bcd60e51b8152600401610d2e9190615112565b60606000614526836002614ea4565b614531906002614e46565b67ffffffffffffffff81111561454957614549614a4d565b6040519080825280601f01601f191660200182016040528015614573576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106145aa576145aa614ee5565b60200101906001600160f81b031916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106145f5576145f5614ee5565b60200101906001600160f81b031916908160001a9053506000614619846002614ea4565b614624906001614e46565b90505b60018111156146a9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061466557614665614ee5565b1a60f81b82828151811061467b5761467b614ee5565b60200101906001600160f81b031916908160001a90535060049490941c936146a28161530b565b9050614627565b5083156125ad5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d2e565b6000614708600a80546001019055565b600060405180606001604052806147208b89896133ca565b815260200188602001516001600160a01b03168152602001614741600a5490565b9052602080820180516001600160a01b039081166000908152600b90935260408084206001015492519091168352909120549192509061478b9061478490613f98565b88886133ca565b11156147c3576040517fb010931500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516020808301516001600160a01b03166000908152600b9091526040812060010180549091906147f5908490614e46565b90915550506020808201516001600160a01b03166000908152600b9091526040812080548b9290614827908490614e46565b9091555050805160058054600090614840908490614e46565b9250508190555088600c60008282546148599190614e46565b9250508190555060405180608001604052808a8152602001336001600160a01b0316815260200182602001516001600160a01b031681526020018967ffffffffffffffff1681525060096000836040015181526020019081526020016000206000820151816000015560208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506060820151816003015590505086602001516001600160a01b0316336001600160a01b031682604001517fb26a67c96199378f459c394f9a442f1a94d19837d1ac17c14e3d6366d98223088d8d866000015187602001518f8f604001518d8d60405161498f989796959493929190615322565b60405180910390a4604001519998505050505050505050565b6000602082840312156149ba57600080fd5b81356001600160e01b0319811681146125ad57600080fd5b6001600160a01b0381168114610d9d57600080fd5b6000602082840312156149f957600080fd5b81356125ad816149d2565b600060208284031215614a1657600080fd5b5035919050565b60008060408385031215614a3057600080fd5b823591506020830135614a42816149d2565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614a8657614a86614a4d565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614ab557614ab5614a4d565b604052919050565b8035600f81900b8114614acf57600080fd5b919050565b600060808284031215614ae657600080fd5b6040516080810181811067ffffffffffffffff82111715614b0957614b09614a4d565b6040528235614b17816149d2565b81526020830135614b27816149d2565b6020820152614b3860408401614abd565b6040820152614b4960608401614abd565b60608201529392505050565b803561ffff81168114614acf57600080fd5b600060208284031215614b7957600080fd5b6125ad82614b55565b60008083601f840112614b9457600080fd5b50813567ffffffffffffffff811115614bac57600080fd5b6020830191508360208260051b8501011115614bc757600080fd5b9250929050565b600080600080600060608688031215614be657600080fd5b8535614bf1816149d2565b9450602086013567ffffffffffffffff80821115614c0e57600080fd5b614c1a89838a01614b82565b90965094506040880135915080821115614c3357600080fd5b50614c4088828901614b82565b969995985093965092949392505050565b600080600060408486031215614c6657600080fd5b8335614c71816149d2565b9250602084013567ffffffffffffffff811115614c8d57600080fd5b614c9986828701614b82565b9497909650939450505050565b600060c08284031215614cb857600080fd5b50919050565b600060208284031215614cd057600080fd5b813567ffffffffffffffff811115614ce757600080fd5b614cf384828501614ca6565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015614d3357835183529284019291840191600101614d17565b50909695505050505050565b60008060408385031215614d5257600080fd5b82359150602083013567ffffffffffffffff811115614d7057600080fd5b614d7c85828601614ca6565b9150509250929050565b60008060008060808587031215614d9c57600080fd5b8435614da7816149d2565b966020860135965060408601359560600135945092505050565b600060208284031215614dd357600080fd5b81516125ad816149d2565b600060208284031215614df057600080fd5b815180151581146125ad57600080fd5b600060208284031215614e1257600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082821015614e4157614e41614e19565b500390565b60008219821115614e5957614e59614e19565b500190565b600061ffff83811690831681811015614e7957614e79614e19565b039392505050565b600060208284031215614e9357600080fd5b815160ff811681146125ad57600080fd5b6000816000190483118215151615614ebe57614ebe614e19565b500290565b600082614ee057634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6000600019821415614f0f57614f0f614e19565b5060010190565b600060208284031215614f2857600080fd5b813567ffffffffffffffff811681146125ad57600080fd5b6000808335601e19843603018112614f5757600080fd5b83018035915067ffffffffffffffff821115614f7257600080fd5b602001915036819003821315614bc757600080fd5b600067ffffffffffffffff808316818516808303821115614faa57614faa614e19565b01949350505050565b6000808335601e19843603018112614fca57600080fd5b83018035915067ffffffffffffffff821115614fe557600080fd5b6020019150600581901b3603821315614bc757600080fd5b600060ff821660ff81141561501457615014614e19565b60010192915050565b60005b83811015615038578181015183820152602001615020565b8381111561448d5750506000910152565b6000825161505b81846020870161501d565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161509d81601785016020880161501d565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516150da81602884016020880161501d565b01602801949350505050565b600081518084526150fe81602086016020860161501d565b601f01601f19169290920160200192915050565b6020815260006125ad60208301846150e6565b60008235605e1983360301811261505b57600080fd5b60006060823603121561514d57600080fd5b615155614a63565b61515e83614b55565b815260208084013561516f816149d2565b82820152604084013567ffffffffffffffff8082111561518e57600080fd5b9085019036601f8301126151a157600080fd5b8135818111156151b3576151b3614a4d565b6151c5601f8201601f19168501614a8c565b915080825236848285010111156151db57600080fd5b808484018584013760009082019093019290925250604082015292915050565b600061ffff808316818516808303821115614faa57614faa614e19565b600181815b8085111561525357816000190482111561523957615239614e19565b8085161561524657918102915b93841c939080029061521d565b509250929050565b60008261526a57506001610a54565b8161527757506000610a54565b816001811461528d5760028114615297576152b3565b6001915050610a54565b60ff8411156152a8576152a8614e19565b50506001821b610a54565b5060208310610133831016604e8410600b84101617156152d6575081810a610a54565b6152e08383615218565b80600019048211156152f4576152f4614e19565b029392505050565b60006125ad60ff84168361525b565b60008161531a5761531a614e19565b506000190190565b8881528760208201528660408201526001600160a01b038616606082015267ffffffffffffffff8516608082015260e060a0820152600061536660e08301866150e6565b82810360c0840152838152838560208301376000602085830101526020601f19601f860116820101915050999850505050505050505056fea264697066735822122067db7a707d6e781088d1ec391c13c78844a6370488f1a5eca731a1efb57d8d0064736f6c634300080a00330000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000002328000000000000000000000000035f210e5d14054e8ae5a6cfa76d643aa200d56e00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103af5760003560e01c80638456cb59116101f4578063c0ddb6921161011a578063df6f9ba7116100ad578063f5eb42dc1161007c578063f5eb42dc14610951578063f62898871461097d578063f78797c0146109a4578063fd6e9217146109cd57600080fd5b8063df6f9ba71461090f578063e4020fdf14610922578063eb0ebfbc14610935578063f0f442601461093e57600080fd5b8063cd0d9ba8116100e9578063cd0d9ba8146108c1578063cda2695a146108d4578063d547741f146108e7578063d9db2b77146108fa57600080fd5b8063c0ddb69214610877578063c2d794441461087f578063c70920bc146108a6578063cd00dd41146108ae57600080fd5b8063a217fddf11610192578063b5106add11610161578063b5106add14610816578063b59a40cb14610829578063bba97d3314610849578063bd2e3ecd1461061857600080fd5b8063a217fddf14610779578063a8c62e7614610781578063abaa9d7014610794578063b02c43d0146107a457600080fd5b80639816f473116101ce5780639816f47314610732578063999927df14610749578063a164c25d1461075c578063a1ac20eb1461076657600080fd5b80638456cb591461065b5780638cad7fbe1461066357806391d14854146106f957600080fd5b80634e813ac4116102d957806361e20a1c1161027757806376490b561161024657806376490b56146106185780637e2cba6f146106225780637e4b8da0146106355780638293744b1461064857600080fd5b806361e20a1c146105755780636e7007e61461059e5780636f307dc3146105bf57806373ae54b5146105d857600080fd5b80635c975abb116102b35780635c975abb1461052f5780635cb0d4061461053a5780635e8486551461054257806361d027b31461054a57600080fd5b80634e813ac41461050c5780634fc78d6814610514578063563233f61461051c57600080fd5b8063364bc15a116103515780633ba52fc6116103205780633ba52fc6146104e05780633f383236146104e85780633f4ba83a146104fb5780634b1533b21461050357600080fd5b8063364bc15a1461048a57806336568abe146104b15780633a98ef39146104c45780633b7d0946146104cd57600080fd5b806319d3ee3d1161038d57806319d3ee3d14610435578063248a9ca31461043e5780632f2ff15d1461046257806333a100ca1461047757600080fd5b806301ffc9a7146103b45780631325d5f1146103dc57806313f6686d146103f9575b600080fd5b6103c76103c23660046149a8565b6109e2565b60405190151581526020015b60405180910390f35b6103eb670de0b6b3a764000081565b6040519081526020016103d3565b6104206104073660046149e7565b600b602052600090815260409020805460019091015482565b604080519283526020830191909152016103d3565b6103eb600e5481565b6103eb61044c366004614a04565b6000908152600160208190526040909120015490565b610475610470366004614a1d565b610a5a565b005b6104756104853660046149e7565b610a85565b6103eb7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b6104756104bf366004614a1d565b610cb4565b6103eb60055481565b6104756104db3660046149e7565b610d45565b610475610da0565b6104756104f6366004614ad4565b610e8d565b610475610ee5565b6103eb600c5481565b610420610f3e565b610475610ffb565b61047561052a366004614b67565b6112ba565b60035460ff166103c7565b6104756113ba565b6104756113cd565b600d5461055d906001600160a01b031681565b6040516001600160a01b0390911681526020016103d3565b6103eb6105833660046149e7565b6001600160a01b03166000908152600b602052604090205490565b600f546105ac9061ffff1681565b60405161ffff90911681526020016103d3565b60035461055d906201000090046001600160a01b031681565b6105ff7f000000000000000000000000000000000000000000000000000000000076a70081565b60405167ffffffffffffffff90911681526020016103d3565b6105ff62dd7c0081565b610475610630366004614b67565b6113e0565b610475610643366004614bce565b6114b3565b610475610656366004614c51565b611551565b610475611630565b6106ba6106713660046149e7565b600060208190529081526040902080546001909101546001600160a01b0382169160ff600160a01b8204811692600160a81b9092041690600f81810b91600160801b9004900b85565b604080516001600160a01b03909616865260ff94851660208701529390921692840192909252600f91820b6060840152900b608082015260a0016103d3565b6103c7610707366004614a1d565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6003546201000090046001600160a01b031661055d565b6104756107573660046149e7565b611687565b6105ff6212750081565b610475610774366004614b67565b61185a565b6103eb600081565b60065461055d906001600160a01b031681565b600354610100900460ff166103c7565b6107e66107b2366004614a04565b600960205260009081526040902080546001820154600283015460039093015491926001600160a01b039182169291169084565b6040516103d394939291909384526001600160a01b03928316602085015291166040830152606082015260800190565b6104756108243660046149e7565b61195c565b61083c610837366004614cbe565b611b40565b6040516103d39190614cfb565b61085c6108573660046149e7565b611be5565b604080519384526020840192909252908201526060016103d3565b6103eb611ca4565b6103eb7f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a81565b6103eb611ce7565b6104756108bc366004614bce565b611e63565b61083c6108cf366004614d3f565b611ef5565b6104756108e2366004614d86565b611faf565b6104756108f5366004614a1d565b6121f5565b600d546105ac90600160a01b900461ffff1681565b61047561091d366004614c51565b61221b565b610475610930366004614c51565b6122ad565b6103eb60045481565b61047561094c3660046149e7565b61233d565b6103eb61095f3660046149e7565b6001600160a01b03166000908152600b602052604090206001015490565b6103eb7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309581565b61055d6109b2366004614a04565b6008602052600090815260409020546001600160a01b031681565b6003546105ac90600160b01b900461ffff1681565b60006001600160e01b031982167f21d982ed000000000000000000000000000000000000000000000000000000001480610a4557506001600160e01b031982167f0fae047800000000000000000000000000000000000000000000000000000000145b80610a545750610a54826125b4565b92915050565b60008281526001602081905260409091200154610a768161261b565b610a808383612625565b505050565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff16610ad457604051631e15456f60e11b815260040160405180910390fd5b6001600160a01b038116610afb57604051634f08b3f360e11b815260040160405180910390fd5b306001600160a01b0316816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b679190614dc1565b6001600160a01b031614610ba7576040517f0251bab500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b031615801590610c335750600660009054906101000a90046001600160a01b03166001600160a01b0316635be9b2d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c339190614dde565b15610c6a576040517fd28a9fdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383169081179091556040517f4da9c22c924692646a21cf1f423781ae3285198dc22e8a6912835d3272b90b3c90600090a250565b6001600160a01b0381163314610d375760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b610d4182826126ac565b5050565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610d9457604051631bacfc6960e01b815260040160405180910390fd5b610d9d8161272f565b50565b3360009081527f10c15aae13326e9b33c92ffbb94efb7492c500f0cca324caca9bd1aca194ed18602052604090205460ff16610def57604051637660f80560e11b815260040160405180910390fd5b600e5480610e29576040517f0a33934000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600e55610e3781612803565b6040518181527fb7eeacba6b133788365610e83d3f130d07b6ef6e78877961f25b3f61fcba07529060200160405180910390a1600d54600354610d9d916001600160a01b03620100009092048216911683612a02565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610edc57604051631bacfc6960e01b815260040160405180910390fd5b610d9d81612a4b565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16610f3457604051631bacfc6960e01b815260040160405180910390fd5b610f3c612e62565b565b60065460009081906001600160a01b0316610f5c5750600091829150565b600354610f7c90600160b01b900461ffff16610f76611ce7565b90612eb4565b9150600660009054906101000a90046001600160a01b03166001600160a01b0316634ad0b6846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff59190614e00565b90509091565b3360009081527f10c15aae13326e9b33c92ffbb94efb7492c500f0cca324caca9bd1aca194ed18602052604090205460ff1661104a57604051637660f80560e11b815260040160405180910390fd5b6006546001600160a01b031661107357604051634f08b3f360e11b815260040160405180910390fd5b60008061107e610f3e565b91509150808214156110bc576040517ff096593000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818111156111af5760006110d08383614e2f565b90507f0000000000000000000000000000000000000000000000008ac7230489e8000081101561111357604051630e33bdb760e11b815260040160405180910390fd5b6006546040516319d1885d60e31b8152600481018390526001600160a01b039091169063ce8c42e890602401600060405180830381600087803b15801561115957600080fd5b505af115801561116d573d6000803e3d6000fd5b505050507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd29816040516111a291815260200190565b60405180910390a1505050565b60006111bb8284614e2f565b90507f0000000000000000000000000000000000000000000000008ac7230489e800008110156111fe57604051630e33bdb760e11b815260040160405180910390fd5b600654600354611221916001600160a01b03620100009092048216911683612a02565b600660009054906101000a90046001600160a01b03166001600160a01b031663e8b5e51f6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561127157600080fd5b505af1158015611285573d6000803e3d6000fd5b505050507fac9f7997c30b6a3cc9c74953898b5de154359339c7ec0d6d70ceb98e55db1a4b816040516111a291815260200190565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661130957604051631e15456f60e11b815260040160405180910390fd5b61ffff81166127101015611349576040517fd25b1e4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff16600160a01b61ffff8416908102919091179091556040519081527f16de0efbdf16c568a60b65978ed774bba4e0c179de55ab8f639527e41fd0c861906020015b60405180910390a150565b60006113c58161261b565b610d9d612ed1565b60006113d88161261b565b610d9d612f10565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661142f57604051631e15456f60e11b815260040160405180910390fd5b61ffff8116612710101561146f576040517f1d05623100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f805461ffff191661ffff83169081179091556040519081527f5b9aa28fc994f782b33ebebd9947795b5bc59a54203466433eac5f8d9e819400906020016113af565b6002805414156115055760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611511612f4a565b6001600160a01b0385166115385760405163f95106ab60e01b815260040160405180910390fd5b6115458585858585612fa2565b50506001600255505050565b6002805414156115a35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280556115af612f4a565b6001600160a01b0383166115d65760405163f95106ab60e01b815260040160405180910390fd5b6115de611ca4565b600c541115611619576040517f0bdd660c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611626838383600061304b565b5050600160025550565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff1661167f57604051631bacfc6960e01b815260040160405180910390fd5b610f3c61310f565b6002805414156116d95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280556116e5612f4a565b6001600160a01b03811661170c5760405163f95106ab60e01b815260040160405180910390fd5b600080600061171a33611be5565b925092509250826000141561175b576040517f58367fe100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611765611ca4565b90506000600554905082600e60008282546117809190614e46565b9091555061178f905085612803565b6003546117ac906201000090046001600160a01b03168787612a02565b336000908152600b6020526040812060010180548692906117ce908490614e2f565b9250508190555083600560008282546117e79190614e2f565b90915550506040805133815260208101879052908101859052606081018490526080810183905260a081018290526001600160a01b038716907f4344accc2a634cf29195bba6fa3d939e3415ecd1ca5c57b322bcf81f920979c89060c00160405180910390a25050600160025550505050565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff166118a957604051631e15456f60e11b815260040160405180910390fd5b61ffff811661271010156118e9576040517f870e44a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405161ffff821681527f40775ca73a6e168d23747bc0200694cf7c99ad2db624d04c095cb59899011a569060200160405180910390a16003805461ffff909216600160b01b027fffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffff909216919091179055565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff166119ab57604051631bacfc6960e01b815260040160405180910390fd5b6001600160a01b0381166119eb576040517f30c9889300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116331415611a2e576040517f25499f7e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a39600082612625565b611a637ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab82612625565b611a8d7ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf60309582612625565b611ab77f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a82612625565b611ac26000336126ac565b611aec7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab336126ac565b611b167ffaf9b26485088dee58863e57c46603d6cdcbadc7475ac6d8910fab0ecf603095336126ac565b610d9d7f1597bc5e34ff090612f53164e4e642d2ab4fc78bffe19ed1b602a0d12559561a336126ac565b6060600280541415611b945760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611ba061314c565b600754611bae816001614e46565b600755600081815260086020526040902080546001600160a01b03191633179055611bd9818461319f565b60016002559392505050565b6001600160a01b0381166000908152600b6020526040812080546001909101548291829182611c12611ca4565b90506000611c238360055484613392565b9050838111611c4057600080600096509650965050505050611c9d565b6000611c4c8583614e2f565b9050611c5b81600554856133ca565b96506000611c6c8860055486613392565b600d54909150611c88908290600160a01b900461ffff16612eb4565b9650611c948782614e2f565b98505050505050505b9193909250565b600080611caf611ce7565b90506000600e54600454611cc39190614e46565b905081811115611cd65760009250505090565b611ce08183614e2f565b9250505090565b6006546000906001600160a01b031615611df257600660009054906101000a90046001600160a01b03166001600160a01b0316634ad0b6846040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d729190614e00565b6003546040516370a0823160e01b8152306004820152620100009091046001600160a01b0316906370a0823190602401602060405180830381865afa158015611dbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de39190614e00565b611ded9190614e46565b905090565b6003546040516370a0823160e01b8152306004820152620100009091046001600160a01b0316906370a0823190602401602060405180830381865afa158015611e3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ded9190614e00565b600280541415611eb55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611ec1612f4a565b6001600160a01b038516611ee85760405163f95106ab60e01b815260040160405180910390fd5b6115458585858585613439565b6060600280541415611f495760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055611f5561314c565b6000838152600860205260409020546001600160a01b03163314611fa5576040517f9049b68600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bd9838361319f565b6002805414156120015760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b600280553360009081527f27365024d84ecf053ef4d13025b66be161b03e90a7fc7ff54f74d5533a9f857e602052604090205460ff1661206d576040517f9aff7af000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61207561314c565b826120ac576040517ff712805800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b621275008210806120bf575062dd7c0082115b156120dd57604051636235acd960e01b815260040160405180910390fd5b60006120e94284614e46565b90506120f9600a80546001019055565b6000612104600a5490565b90506121113387876134c7565b600061211e8787866135ff565b604080516080810182528281523360208083019182526000838501818152606085018a815289835260099093529481209351845591516001840180546001600160a01b03199081166001600160a01b0393841617909155945160028501805490961691161790935591516003909101556004805492935083929091906121a5908490614e46565b90915550506040805182815260208101859052339184917f7ca3497675c6d3ec00b1edc13b8e0b06169ee96a663ab25a74af7da53378f09e910160405180910390a3505060016002555050505050565b600082815260016020819052604090912001546122118161261b565b610a8083836126ac565b60028054141561226d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b60028055612279612f4a565b6001600160a01b0383166122a05760405163f95106ab60e01b815260040160405180910390fd5b611626838383600161304b565b6002805414156122ff5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d2e565b6002805561230b612f4a565b6001600160a01b0383166123325760405163f95106ab60e01b815260040160405180910390fd5b6116268383836137df565b3360009081527f473683a5a3e156e4e3a6c3a7a6d9e3d4a58f9392a3bba04b84ad6221b3f7f356602052604090205460ff1661238c57604051631e15456f60e11b815260040160405180910390fd5b6001600160a01b0381166123cc576040517f6b5e63d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0383169081179091556040517f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a250565b612710101590565b8015806124985750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612472573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124969190614e00565b155b61250a5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610d2e565b6040516001600160a01b038316602482015260448101829052610a809084907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915261385a565b60606125aa848460008561393f565b90505b9392505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610a5457507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610a54565b610d9d8133613a7c565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16610d415760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff1615610d415760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b03818116600090815260208190526040902054166127965760405162461bcd60e51b815260206004820152601360248201527f706f6f6c20646f6573206e6f74206578697374000000000000000000000000006044820152606401610d2e565b6001600160a01b03811660008181526020819052604080822080547fffffffffffffffffffff00000000000000000000000000000000000000000000168155600101829055517f94be23730c4398d20227f4e457cf92307b13e5956f2a958e2e368ad44e2f826b9190a250565b6003546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a0823190602401602060405180830381865afa158015612852573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128769190614e00565b9050808211612883575050565b600660009054906101000a90046001600160a01b03166001600160a01b0316639af2e6356040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fa9190614dde565b61291757604051635f842b9960e11b815260040160405180910390fd5b60035460009061294b9061293890600160b01b900461ffff16612710614e5e565b84612941611ce7565b610f769190614e2f565b905060008261295a8386614e46565b6129649190614e2f565b6006546040516319d1885d60e31b8152600481018390529192506001600160a01b03169063ce8c42e890602401600060405180830381600087803b1580156129ab57600080fd5b505af11580156129bf573d6000803e3d6000fd5b505050507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd29816040516129f491815260200190565b60405180910390a150505050565b6040516001600160a01b038316602482015260448101829052610a809084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161254f565b80516001600160a01b039081166000908152602081905260409020541615612ab55760405162461bcd60e51b815260206004820152601d60248201527f746f6b656e20616c7265616479206861732061207377617020706f6f6c0000006044820152606401610d2e565b6003546201000090046001600160a01b03166001600160a01b031681602001516001600160a01b031663c661065783606001516fffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401612b1891815260200190565b602060405180830381865afa158015612b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b599190614dc1565b6001600160a01b031614612bd55760405162461bcd60e51b815260206004820152602c60248201527f5f756e6465726c79696e674920646f6573206e6f74206d6174636820756e646560448201527f726c79696e6720746f6b656e00000000000000000000000000000000000000006064820152608401610d2e565b600081600001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3d9190614e81565b60ff1690506000612c5d6003546001600160a01b03620100009091041690565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cbe9190614e81565b6040805160a0810182526020808701516001600160a01b03908116835260ff8781168385019081529581168486018181528a870151600f90810b60608089019182528d015190910b608088019081528c5186166000908152968790529790952095518654985191518416600160a81b027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff92909416600160a01b027fffffffffffffffffffffff00000000000000000000000000000000000000000090991694169390931796909617919091161782555191516fffffffffffffffffffffffffffffffff908116600160801b029216919091176001909101559050612ddf612dd56003546001600160a01b03620100009091041690565b8460200151613afc565b612df183600001518460200151613afc565b82602001516001600160a01b031683600001516001600160a01b03167f1576b5fcfa863788d9ef6558cab7aa68d4b48c44eb7bacaf1a8ac6d6b9d26f9485604001518660600151604051612e55929190600f92830b8152910b602082015260400190565b60405180910390a3505050565b612e6a613b8d565b6003805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000612710612ec761ffff841685614ea4565b6125ad9190614ec3565b612ed9612f4a565b6003805461ff0019166101001790557f92493a2527e1a0e4214d0f98b1bedbdc2b53ef81ee4578756108fef0deff09ee612e973390565b612f18613bdf565b6003805461ff00191690557fc47f9c912614e04aeb80c7e6cdc73c0f4368cde4bf42df3b26ad96b898913c7d33612e97565b600354610100900460ff1615610f3c5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a20457869745061757365640000000000000000000000006044820152606401610d2e565b6005546000612faf611ca4565b9050600085815b8181101561301957612ffd898983818110612fd357612fd3614ee5565b9050602002013586868d60008c8c88818110612ff157612ff1614ee5565b90506020020135613c36565b6130079084614e46565b925061301281614efb565b9050612fb6565b5061302382612803565b600354613040906201000090046001600160a01b03168a84612a02565b505050505050505050565b6005546000613058611ca4565b9050600084815b818110156130de576000600960008a8a8581811061307f5761307f614ee5565b9050602002013581526020019081526020016000206000015490506130c08989848181106130af576130af614ee5565b9050602002013587878d8b86613c36565b6130ca9085614e46565b935050806130d790614efb565b905061305f565b506130e882612803565b600354613105906201000090046001600160a01b03168984612a02565b5050505050505050565b61311761314c565b6003805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612e973390565b60035460ff1615610f3c5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610d2e565b606060408201356131dc576040517fab59997c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000076a700166132176040840160208501614f16565b67ffffffffffffffff161080613248575062dd7c0061323c6040840160208501614f16565b67ffffffffffffffff16115b1561326657604051636235acd960e01b815260040160405180910390fd5b60036132756080840184614f40565b905010156132af576040517f3867858900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006132bc600c54613f98565b905060006132c8611ca4565b905080821115613304576040517f08063cab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61331f3361331560208701876149e7565b86604001356134c7565b600061334061333160208701876149e7565b86604001358760a001356135ff565b90506000426133556040880160208901614f16565b61335f9190614f87565b905061338783838361337460608b018b614fb3565b61338160808d018d614f40565b8e613fb7565b979650505050505050565b600083158061339f575082155b806133a8575081155b156133b5575060006125ad565b826133c08584614ea4565b6125aa9190614ec3565b6000836133d9575060006125ad565b826133f7576133f0670de0b6b3a764000085614ea4565b90506125ad565b8161342e576040517ff91b6e7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816133c08486614ea4565b600083815b818160ff1610156134b357600087878360ff1681811061346057613460614ee5565b905060200201359050600086868460ff1681811061348057613480614ee5565b9050602002013590506134948a83836141f9565b61349e8186614e46565b94505050806134ac90614ffd565b905061343e565b506134be87836143d6565b50505050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561350e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135329190614e00565b90506135496001600160a01b03841685308561443c565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015613590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135b49190614e00565b90506135c08383614e46565b81146135f8576040517f2a25a38b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b60008061361b6003546001600160a01b03620100009091041690565b9050806001600160a01b0316856001600160a01b0316141561364057839150506125ad565b6001600160a01b03808616600090815260208190526040902080549091166136aa5760405162461bcd60e51b815260206004820152601660248201527f6e6f6e2d6578697374696e67207377617020706f6f6c000000000000000000006044820152606401610d2e565b80546000906136cf90879060ff600160a01b8204811691600160a81b90041688614493565b825460018401546040517fa6417ed6000000000000000000000000000000000000000000000000000000008152600f82810b6004830152600160801b90920490910b602482015260448101899052606481018390529192506001600160a01b03169063a6417ed6906084016020604051808303816000875af1158015613759573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061377d9190614e00565b9350826001600160a01b0316876001600160a01b03167ffa2dda1cc1b86e41239702756b13effbc1a092b5c57e3ad320fbe4f3b13fe23588876040516137cd929190918252602082015260400190565b60405180910390a35050509392505050565b600081815b818160ff16101561384f57600085858360ff1681811061380657613806614ee5565b602090810292909201356000818152600990935260409092205491925061383090508883836141f9565b61383a8186614e46565b945050508061384890614ffd565b90506137e4565b506135f885836143d6565b60006138af826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661259b9092919063ffffffff16565b805190915015610a8057808060200190518101906138cd9190614dde565b610a805760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d2e565b6060824710156139b75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610d2e565b6001600160a01b0385163b613a0e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d2e565b600080866001600160a01b03168587604051613a2a9190615049565b60006040518083038185875af1925050503d8060008114613a67576040519150601f19603f3d011682016040523d82523d6000602084013e613a6c565b606091505b50915091506133878282866144de565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16610d4157613aba816001600160a01b03166014614517565b613ac5836020614517565b604051602001613ad6929190615065565b60408051601f198184030181529082905262461bcd60e51b8252610d2e91600401615112565b604051636eb1769f60e11b81523060048201526001600160a01b0382811660248301526000919084169063dd62ed3e90604401602060405180830381865afa158015613b4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b709190614e00565b905080610a8057610a806001600160a01b0384168360001961241e565b60035460ff16610f3c5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610d2e565b600354610100900460ff16610f3c5760405162461bcd60e51b815260206004820152601860248201527f5061757361626c653a206e6f74204578697450617573656400000000000000006044820152606401610d2e565b6000868152600960205260408120600101546001600160a01b03163314613c89576040517fff40736100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008781526009602090815260408083208151608081018352815481526001808301546001600160a01b039081168387015260028401541682850181905260039093015460608301908152928652600b85529483902083518085019094528054845290940154928201929092529051421015613d31576040517f0f6268c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516001600160a01b0316613d75576040517fde8b700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151841115613d9757604051635bcd1de160e11b815260040160405180910390fd5b6000613da4858a8a6133ca565b82516020840151919250600091613dbb9088614ea4565b613dc59190614ec3565b905086158015613dd457508082115b15613e0b576040517f7df2e7ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81878015613e1857508183115b15613e205750805b6040808601516001600160a01b03166000908152600b6020529081206001018054839290613e4f908490614e2f565b90915550506040808601516001600160a01b03166000908152600b6020529081208054899290613e80908490614e2f565b925050819055508060056000828254613e999190614e2f565b9250508190555086600c6000828254613eb29190614e2f565b9091555050845187148015613efb5760008d81526009602052604081208181556001810180546001600160a01b0319908116909155600282018054909116905560030155613f1f565b60008d815260096020526040812080548a9290613f19908490614e2f565b90915550505b6000613f2c838e8e613392565b90508a6001600160a01b03168e7f14e79002ce0df3ba4381425c583d4c9984c7e2910d3e7e088377db997773153e858486604051613f7f9392919092835260208301919091521515604082015260600190565b60405180910390a39d9c50505050505050505050505050565b600f54600090613fad90839061ffff16612eb4565b610a549083614e2f565b6040805160a0810182526005548152602081018a90526000918101829052606081810183905260808201879052918667ffffffffffffffff811115613ffe57613ffe614a4d565b604051908082528060200260200182016040528015614027578160200160208202803683370190505b50905060005b82608001518110156141a157600089898381811061404d5761404d614ee5565b905060200281019061405f9190615125565b6140689061513b565b805190915061ffff166140a7576040517f4f287e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516001600160a01b03166140eb576040517fd444696b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600185608001516140fe9190614e2f565b8314614116578151614111908e90612eb4565b614125565b6060850151614125908e614e2f565b905061413f87828e8589600001518a602001518f8f6146f8565b84848151811061415157614151614ee5565b6020908102919091010152815160408601805161416f9083906151fb565b61ffff1690525060608501805182919061418a908390614e46565b90525061419a9150829050614efb565b905061402d565b506141b5826040015161ffff166127101490565b6141eb576040517fd7b03e8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9a9950505050505050505050565b60008281526009602090815260409182902082516080810184528154815260018201546001600160a01b0390811693820184905260028301541693810193909352600301546060830152331461427b576040517f5040aaa300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42816060015111156142b9576040517f4cf1715e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408101516001600160a01b0316156142fe576040517f6f15ccf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805182111561432057604051635bcd1de160e11b815260040160405180910390fd5b80516040805184815291841460208301819052916001600160a01b0387169186917f8bb6f71206f2ec5e5a42539a0d677bb7e48844b6cefdad5d7139fd68568d7299910160405180910390a38061439b576000848152600960205260408120805485929061438f908490614e2f565b90915550505050505050565b50505060009081526009602052604081208181556001810180546001600160a01b031990811690915560028201805490911690556003015550565b6143de611ce7565b8111156143fe57604051635f842b9960e11b815260040160405180910390fd5b80600460008282546144109190614e2f565b9091555061441f905081612803565b600354610d41906201000090046001600160a01b03168383612a02565b6040516001600160a01b038085166024830152831660448201526064810182905261448d9085907f23b872dd000000000000000000000000000000000000000000000000000000009060840161254f565b50505050565b60006144a084600a6152fc565b6144ac90612710614ea4565b6144b784600a6152fc565b6144c18488614ea4565b6144cb9190614ea4565b6144d59190614ec3565b95945050505050565b606083156144ed5750816125ad565b8251156144fd5782518084602001fd5b8160405162461bcd60e51b8152600401610d2e9190615112565b60606000614526836002614ea4565b614531906002614e46565b67ffffffffffffffff81111561454957614549614a4d565b6040519080825280601f01601f191660200182016040528015614573576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106145aa576145aa614ee5565b60200101906001600160f81b031916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106145f5576145f5614ee5565b60200101906001600160f81b031916908160001a9053506000614619846002614ea4565b614624906001614e46565b90505b60018111156146a9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061466557614665614ee5565b1a60f81b82828151811061467b5761467b614ee5565b60200101906001600160f81b031916908160001a90535060049490941c936146a28161530b565b9050614627565b5083156125ad5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d2e565b6000614708600a80546001019055565b600060405180606001604052806147208b89896133ca565b815260200188602001516001600160a01b03168152602001614741600a5490565b9052602080820180516001600160a01b039081166000908152600b90935260408084206001015492519091168352909120549192509061478b9061478490613f98565b88886133ca565b11156147c3576040517fb010931500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516020808301516001600160a01b03166000908152600b9091526040812060010180549091906147f5908490614e46565b90915550506020808201516001600160a01b03166000908152600b9091526040812080548b9290614827908490614e46565b9091555050805160058054600090614840908490614e46565b9250508190555088600c60008282546148599190614e46565b9250508190555060405180608001604052808a8152602001336001600160a01b0316815260200182602001516001600160a01b031681526020018967ffffffffffffffff1681525060096000836040015181526020019081526020016000206000820151816000015560208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506060820151816003015590505086602001516001600160a01b0316336001600160a01b031682604001517fb26a67c96199378f459c394f9a442f1a94d19837d1ac17c14e3d6366d98223088d8d866000015187602001518f8f604001518d8d60405161498f989796959493929190615322565b60405180910390a4604001519998505050505050505050565b6000602082840312156149ba57600080fd5b81356001600160e01b0319811681146125ad57600080fd5b6001600160a01b0381168114610d9d57600080fd5b6000602082840312156149f957600080fd5b81356125ad816149d2565b600060208284031215614a1657600080fd5b5035919050565b60008060408385031215614a3057600080fd5b823591506020830135614a42816149d2565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614a8657614a86614a4d565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614ab557614ab5614a4d565b604052919050565b8035600f81900b8114614acf57600080fd5b919050565b600060808284031215614ae657600080fd5b6040516080810181811067ffffffffffffffff82111715614b0957614b09614a4d565b6040528235614b17816149d2565b81526020830135614b27816149d2565b6020820152614b3860408401614abd565b6040820152614b4960608401614abd565b60608201529392505050565b803561ffff81168114614acf57600080fd5b600060208284031215614b7957600080fd5b6125ad82614b55565b60008083601f840112614b9457600080fd5b50813567ffffffffffffffff811115614bac57600080fd5b6020830191508360208260051b8501011115614bc757600080fd5b9250929050565b600080600080600060608688031215614be657600080fd5b8535614bf1816149d2565b9450602086013567ffffffffffffffff80821115614c0e57600080fd5b614c1a89838a01614b82565b90965094506040880135915080821115614c3357600080fd5b50614c4088828901614b82565b969995985093965092949392505050565b600080600060408486031215614c6657600080fd5b8335614c71816149d2565b9250602084013567ffffffffffffffff811115614c8d57600080fd5b614c9986828701614b82565b9497909650939450505050565b600060c08284031215614cb857600080fd5b50919050565b600060208284031215614cd057600080fd5b813567ffffffffffffffff811115614ce757600080fd5b614cf384828501614ca6565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015614d3357835183529284019291840191600101614d17565b50909695505050505050565b60008060408385031215614d5257600080fd5b82359150602083013567ffffffffffffffff811115614d7057600080fd5b614d7c85828601614ca6565b9150509250929050565b60008060008060808587031215614d9c57600080fd5b8435614da7816149d2565b966020860135965060408601359560600135945092505050565b600060208284031215614dd357600080fd5b81516125ad816149d2565b600060208284031215614df057600080fd5b815180151581146125ad57600080fd5b600060208284031215614e1257600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082821015614e4157614e41614e19565b500390565b60008219821115614e5957614e59614e19565b500190565b600061ffff83811690831681811015614e7957614e79614e19565b039392505050565b600060208284031215614e9357600080fd5b815160ff811681146125ad57600080fd5b6000816000190483118215151615614ebe57614ebe614e19565b500290565b600082614ee057634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6000600019821415614f0f57614f0f614e19565b5060010190565b600060208284031215614f2857600080fd5b813567ffffffffffffffff811681146125ad57600080fd5b6000808335601e19843603018112614f5757600080fd5b83018035915067ffffffffffffffff821115614f7257600080fd5b602001915036819003821315614bc757600080fd5b600067ffffffffffffffff808316818516808303821115614faa57614faa614e19565b01949350505050565b6000808335601e19843603018112614fca57600080fd5b83018035915067ffffffffffffffff821115614fe557600080fd5b6020019150600581901b3603821315614bc757600080fd5b600060ff821660ff81141561501457615014614e19565b60010192915050565b60005b83811015615038578181015183820152602001615020565b8381111561448d5750506000910152565b6000825161505b81846020870161501d565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161509d81601785016020880161501d565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516150da81602884016020880161501d565b01602801949350505050565b600081518084526150fe81602086016020860161501d565b601f01601f19169290920160200192915050565b6020815260006125ad60208301846150e6565b60008235605e1983360301811261505b57600080fd5b60006060823603121561514d57600080fd5b615155614a63565b61515e83614b55565b815260208084013561516f816149d2565b82820152604084013567ffffffffffffffff8082111561518e57600080fd5b9085019036601f8301126151a157600080fd5b8135818111156151b3576151b3614a4d565b6151c5601f8201601f19168501614a8c565b915080825236848285010111156151db57600080fd5b808484018584013760009082019093019290925250604082015292915050565b600061ffff808316818516808303821115614faa57614faa614e19565b600181815b8085111561525357816000190482111561523957615239614e19565b8085161561524657918102915b93841c939080029061521d565b509250929050565b60008261526a57506001610a54565b8161527757506000610a54565b816001811461528d5760028114615297576152b3565b6001915050610a54565b60ff8411156152a8576152a8614e19565b50506001821b610a54565b5060208310610133831016604e8410600b84101617156152d6575081810a610a54565b6152e08383615218565b80600019048211156152f4576152f4614e19565b029392505050565b60006125ad60ff84168361525b565b60008161531a5761531a614e19565b506000190190565b8881528760208201528660408201526001600160a01b038616606082015267ffffffffffffffff8516608082015260e060a0820152600061536660e08301866150e6565b82810360c0840152838152838560208301376000602085830101526020601f19601f860116820101915050999850505050505050505056fea264697066735822122067db7a707d6e781088d1ec391c13c78844a6370488f1a5eca731a1efb57d8d0064736f6c634300080a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000002328000000000000000000000000035f210e5d14054e8ae5a6cfa76d643aa200d56e00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _underlying (address): 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0
Arg [1] : _minLockPeriod (uint64): 7776000
Arg [2] : _investPct (uint16): 9000
Arg [3] : _treasury (address): 0x035F210e5d14054E8AE5A6CFA76d643aA200D56E
Arg [4] : _admin (address): 0x84f67f75DAf6D57Aef500E0c85C77B7b3bBc92A9
Arg [5] : _perfFeePct (uint16): 0
Arg [6] : _lossTolerancePct (uint16): 200
Arg [7] : _swapPools (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0
Arg [1] : 000000000000000000000000000000000000000000000000000000000076a700
Arg [2] : 0000000000000000000000000000000000000000000000000000000000002328
Arg [3] : 000000000000000000000000035f210e5d14054e8ae5a6cfa76d643aa200d56e
Arg [4] : 00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 00000000000000000000000000000000000000000000000000000000000000c8
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [9] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [10] : 000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [14] : 000000000000000000000000ed279fdd11ca84beef15af5d39bb4d4bee23f0ca
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000


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
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.