ETH Price: $2,746.68 (+0.17%)

Contract

0x4E9FbDa4Dc1a207Db97e2BD66Fd1e8837c4DdD36
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw191749092024-02-07 7:44:35379 days ago1707291875IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0038713925.01156909
Withdraw149802532022-06-17 16:53:33979 days ago1655484813IN
0x4E9FbDa4...37c4DdD36
0 ETH0.004413363.66013813
Withdraw149730642022-06-16 11:29:24980 days ago1655378964IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0036970227.03429311
Withdraw149729222022-06-16 10:54:35980 days ago1655376875IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0035725126.12387129
Approve149705272022-06-16 0:57:10981 days ago1655341030IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0013640229.28785087
Approve149285562022-06-08 19:56:04988 days ago1654718164IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0036811679.04077421
Approve149208562022-06-07 12:39:14989 days ago1654605554IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0014649631.45524906
Withdraw149207452022-06-07 12:13:58989 days ago1654604038IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0059290937.07098997
Withdraw149203772022-06-07 10:42:12989 days ago1654598532IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0034747525.68584416
Approve149164382022-06-06 18:35:58990 days ago1654540558IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0011251542.18339949
Approve149164352022-06-06 18:35:19990 days ago1654540519IN
0x4E9FbDa4...37c4DdD36
0 ETH0.0017674937.9511288

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AaveV2StablecoinCellar

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion
File 1 of 14 : AaveV2StablecoinCellar.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
import { SafeTransferLib } from "@rari-capital/solmate/src/utils/SafeTransferLib.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IAaveIncentivesController } from "./interfaces/IAaveIncentivesController.sol";
import { IStakedTokenV2 } from "./interfaces/IStakedTokenV2.sol";
import { ICurveSwaps } from "./interfaces/ICurveSwaps.sol";
import { ISushiSwapRouter } from "./interfaces/ISushiSwapRouter.sol";
import { IGravity } from "./interfaces/IGravity.sol";
import { ILendingPool } from "./interfaces/ILendingPool.sol";
import { MathUtils } from "./utils/MathUtils.sol";

import "./Errors.sol";
import { IAaveV2StablecoinCellar } from "./interfaces/IAaveV2StablecoinCellar.sol";

/**
 * @title Sommelier Aave V2 Stablecoin Cellar
 * @notice Dynamic ERC4626 that changes positions to always get the best yield for stablecoins on Aave.
 * @author Brian Le
 */
contract AaveV2StablecoinCellar is IAaveV2StablecoinCellar, ERC20, Ownable {
    using SafeTransferLib for ERC20;
    using MathUtils for uint256;

    /**
     * @notice The asset that makes up the cellar's holding pool. Will change whenever the cellar
     *         rebalances into a new position.
     * @dev The cellar denotes its inactive assets in this token. While it waits in the holding pool
     *      to be entered into a position, it is used as exit liquidity from those redeeming their
     *      shares for capital efficiency.
     */
    ERC20 public asset;

    /**
     * @notice An interest-bearing derivative of the current asset returned by Aave for lending
     *         the current asset. Represents cellar's portion of active assets earning yield in a
     *         lending position.
     */
    ERC20 public assetAToken;

    /**
     * @notice The decimals of precision used by the current asset.
     * @dev Since stablecoins don't use the standard 18 decimals of precision (eg. USDC and USDT),
     *      we cache this to use for decimal conversions when performing calculations and storing data.
     */
    uint8 public assetDecimals;

    /**
     * @notice Mapping from a user's address to all their deposits and balances.
     * @dev Used to determining which of a user's shares are active (ie. entered into a position earning
     *      yield vs inactive (ie. waiting in the holding pool to be entered into a position and not
     *      earning yield).
     */
    mapping(address => UserDeposit[]) public userDeposits;

    /**
     * @notice Mapping from a user's address to the index of their first non-zero deposit in `userDeposits`.
     * @dev Saves gas when looping through all of a user's deposits.
     */
    mapping(address => uint256) public currentDepositIndex;

    /**
     * @notice Whether an asset position is trusted or not. Prevents cellar from rebalancing into an
     *         asset that has not been trusted by the users. Trusting / distrusting of an asset is done
     *         through governance.
     */
    mapping(address => bool) public isTrusted;

    /**
     * @notice Last time all inactive assets were entered into a strategy and made active. Used to
     *         determining which of a user's shares are active.
     */
    uint256 public lastTimeEnteredPosition;

    /**
     * @notice The value fees are divided by to get a percentage. Represents the maximum percent (100%).
     */
    uint256 public constant DENOMINATOR = 100_00;

    /**
     * @notice The percentage of platform fees taken off of active assets over a year.
     */
    uint256 public constant PLATFORM_FEE = 1_00; // 1%

    /**
     * @notice The percentage of performance fees taken off of cellar gains.
     */
    uint256 public constant PERFORMANCE_FEE = 10_00; // 10%

    /**
     * @notice Stores fee-related data.
     */
    IAaveV2StablecoinCellar.Fees public fees;

    /**
     * @notice Cosmos address of the fee distributor as a hex value.
     * @dev The Gravity contract expects a 32-byte value formatted in a specific way.
     */
    bytes32 public feesDistributor = hex"000000000000000000000000b813554b423266bbd4c16c32fa383394868c1f55";

    /**
     * @notice Maximum amount of assets that can be managed by the cellar. Denominated in the same decimals
     *         as the current asset.
     * @dev Set to `type(uint256).max` to have no limit.
     */
    uint256 public liquidityLimit;

    /**
     * @notice Maximum amount of assets per wallet. Denominated in the same decimals as the current asset.
     * @dev Set to `type(uint256).max` to have no limit.
     */
    uint256 public depositLimit;

    /**
     * @notice Whether or not the contract is shutdown in case of an emergency.
     */
    bool public isShutdown;

    // ======================================== INITIALIZATION ========================================

    /**
     * @notice Curve Registry Exchange contract. Used for rebalancing positions.
     */
    ICurveSwaps public immutable curveRegistryExchange; // 0x81C46fECa27B31F3ADC2b91eE4be9717d1cd3DD7

    /**
     * @notice SushiSwap Router V2 contract. Used for reinvesting rewards back into the current position.
     */
    ISushiSwapRouter public immutable sushiswapRouter; // 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F

    /**
     * @notice Aave Lending Pool V2 contract. Used to deposit and withdraw from the current position.
     */
    ILendingPool public immutable lendingPool; // 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9

    /**
     * @notice Aave Incentives Controller V2 contract. Used to claim and unstake rewards to reinvest.
     */
    IAaveIncentivesController public immutable incentivesController; // 0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5

    /**
     * @notice Cosmos Gravity Bridge contract. Used to transfer fees to `feeDistributor` on the Sommelier chain.
     */
    IGravity public immutable gravityBridge; // 0x69592e6f9d21989a043646fE8225da2600e5A0f7

    /**
     * @notice stkAAVE address. Used to swap rewards to the current asset to reinvest.
     */
    IStakedTokenV2 public immutable stkAAVE; // 0x4da27a545c0c5B758a6BA100e3a049001de870f5

    /**
     * @notice AAVE address. Used to swap rewards to the current asset to reinvest.
     */
    ERC20 public immutable AAVE; // 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9

    /**
     * @notice WETH address. Used to swap rewards to the current asset to reinvest.
     */
    ERC20 public immutable WETH; // 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

    /**
     * @dev Owner will be set to the Gravity Bridge, which relays instructions from the Steward
     *      module to the cellars.
     *      https://github.com/PeggyJV/steward
     *      https://github.com/cosmos/gravity-bridge/blob/main/solidity/contracts/Gravity.sol
     * @param _asset current asset managed by the cellar
     * @param _approvedPositions list of approved positions to start with
     * @param _curveRegistryExchange Curve registry exchange
     * @param _sushiswapRouter Sushiswap V2 router address
     * @param _lendingPool Aave V2 lending pool address
     * @param _incentivesController _incentivesController
     * @param _gravityBridge Cosmos Gravity Bridge address
     * @param _stkAAVE stkAAVE address
     * @param _AAVE AAVE address
     * @param _WETH WETH address
     */
    constructor(
        ERC20 _asset,
        address[] memory _approvedPositions,
        ICurveSwaps _curveRegistryExchange,
        ISushiSwapRouter _sushiswapRouter,
        ILendingPool _lendingPool,
        IAaveIncentivesController _incentivesController,
        IGravity _gravityBridge,
        IStakedTokenV2 _stkAAVE,
        ERC20 _AAVE,
        ERC20 _WETH
    ) ERC20("Sommelier Aave V2 Stablecoin Cellar LP Token", "aave2-CLR-S", 18) {
        // Initialize immutables.
        curveRegistryExchange =  _curveRegistryExchange;
        sushiswapRouter = _sushiswapRouter;
        lendingPool = _lendingPool;
        incentivesController = _incentivesController;
        gravityBridge = _gravityBridge;
        stkAAVE = _stkAAVE;
        AAVE = _AAVE;
        WETH = _WETH;

        // Initialize asset.
        isTrusted[address(_asset)] = true;
        _updatePosition(address(_asset));

        // Initialize limits.
        uint256 powOfAssetDecimals = 10**assetDecimals;
        liquidityLimit = 5_000_000 * powOfAssetDecimals;
        depositLimit = 50_000 * powOfAssetDecimals;

        // Initialize approved positions.
        for (uint256 i; i < _approvedPositions.length; i++) isTrusted[_approvedPositions[i]] = true;

        // Transfer ownership to the Gravity Bridge.
        transferOwnership(address(_gravityBridge));
    }

    // =============================== DEPOSIT/WITHDRAWAL OPERATIONS ===============================

    /**
     * @notice Deposits assets and mints the shares to receiver.
     * @param assets amount of assets to deposit
     * @param receiver address receiving the shares
     * @return shares amount of shares minted
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
        // Depositing above balance will only deposit balance.
        uint256 depositableAssets = asset.balanceOf(msg.sender);
        if (assets > depositableAssets) assets = depositableAssets;

        (, shares) = _deposit(assets, 0, receiver);
    }

    /**
     * @notice Mints shares to receiver by depositing assets.
     * @param shares amount of shares to mint
     * @param receiver address receiving the shares
     * @return assets amount of assets deposited
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets) {
        // Depositing above balance will only deposit balance.
        uint256 mintableShares = previewDeposit(asset.balanceOf(msg.sender));
        if (shares > mintableShares) shares = mintableShares;

        (assets, ) = _deposit(0, shares, receiver);
    }


    function _deposit(uint256 assets, uint256 shares, address receiver) internal returns (uint256, uint256) {
        if (isShutdown) revert STATE_ContractShutdown();

        // Must calculate before assets are transferred in.
        shares > 0 ? assets = previewMint(shares) : shares = previewDeposit(assets);

        // Prevent event spamming and user deposit spamming.
        if (shares == 0) revert USR_ZeroShares();

        // Enforce global liquidity restrictions and deposit restrictions per wallet.
        if (assets > maxDeposit(receiver)) revert USR_DepositRestricted(assets, maxDeposit(receiver));

        // Transfers assets into the cellar.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        // Mint user tokens that represents their share of the cellar's assets.
        _mint(receiver, shares);

        // Store the user's deposit data. This will be used later on when the user wants to withdraw
        // their assets or transfer their shares.
        UserDeposit[] storage deposits = userDeposits[receiver];
        deposits.push(UserDeposit({
            // Always store asset amounts with 18 decimals of precision regardless of the asset's
            // decimals. This is so we can still use this data even after rebalancing to different
            // asset.
            assets: uint112(assets.changeDecimals(assetDecimals, decimals)),
            shares: uint112(shares),
            timeDeposited: uint32(block.timestamp)
        }));

        emit Deposit(
            msg.sender,
            receiver,
            address(asset),
            assets,
            shares
        );

        return (assets, shares);
    }

    /**
     * @notice Withdraws assets to receiver by redeeming shares from owner.
     * @param assets amount of assets being withdrawn
     * @param receiver address of account receiving the assets
     * @param owner address of the owner of the shares being redeemed
     * @return shares amount of shares redeemed
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares) {
        // Ensures proceeding calculations are done with a standard 18 decimals of precision. Will
        // change back to the using the asset's usual decimals of precision when transferring assets
        // after all calculations are done.
        assets = assets.changeDecimals(assetDecimals, decimals);

        // Withdrawing above balance will only withdraw balance.
        (, shares) = _withdraw(assets, receiver, owner);
    }

    /**
     * @notice Redeems shares from owner to withdraw assets to receiver.
     * @param shares amount of shares redeemed
     * @param receiver address of account receiving the assets
     * @param owner address of the owner of the shares being redeemed
     * @return assets amount of assets sent to receiver
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets) {
        // Withdrawing above balance will only withdraw balance.
        (assets, ) = _withdraw(_convertToAssets(shares), receiver, owner);
    }

    /**
     * @dev `assets` must be passed in with 18 decimals of precision. Must extend/truncate decimals of
     *       the amount passed in if necessary to ensure this is true.
     */
    function _withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) internal returns (uint256, uint256) {
        if (balanceOf[owner] == 0) revert USR_ZeroShares();
        if (assets == 0) revert USR_ZeroAssets();

        // Tracks amount of shares to redeem.
        uint256 shares;

        // Retrieve the user's deposits to begin looping through them, generally from oldest to
        // newest deposits. This may not be the case if shares have been transferred to the owner,
        // which will be added to the end of the owner's deposits regardless of time deposited.
        UserDeposit[] storage deposits = userDeposits[owner];

        // Tracks the amount of assets left to withdraw. Updated at the end of each loop.
        uint256 leftToWithdraw = assets;

        // Saves gas by avoiding calling `_convertToAssets` on active shares during each loop.
        uint256 exchangeRate = _convertToAssets(1e18);

        for (uint256 i = currentDepositIndex[owner]; i < deposits.length; i++) {
            UserDeposit storage d = deposits[i];

            // Whether or not deposited shares are active or inactive.
            bool isActive = d.timeDeposited < lastTimeEnteredPosition;

            // If shares are active, convert them to the amount of assets they're worth to get the
            // maximum amount of assets withdrawable from this deposit.
            uint256 dAssets = isActive ? uint256(d.shares).mulWadDown(exchangeRate) : d.assets;

            // Determine the amount of assets and shares to withdraw from this deposit.
            uint256 withdrawnAssets = MathUtils.min(leftToWithdraw, dAssets);
            uint256 withdrawnShares = uint256(d.shares).mulDivUp(withdrawnAssets, dAssets);

            // For active shares, deletes the deposit data we don't need anymore for a gas refund.
            if (isActive) {
                delete d.assets;
                delete d.timeDeposited;
            } else {
                // Substract the amount of assets taken for this withdraw.
                d.assets -= uint112(withdrawnAssets);
            }

            // Subtract shares withdrawn and add to total.
            d.shares -= uint112(withdrawnShares);
            shares += withdrawnShares;

            // Update the counter of assets left to withdraw.
            leftToWithdraw -= withdrawnAssets;

            // Break if this is the last deposit or there is nothing left to withdraw.
            if (i == deposits.length - 1 || leftToWithdraw == 0) {
                // Store the user's next non-zero deposit to save gas on future looping.
                currentDepositIndex[owner] = d.shares != 0 ? i : i+1;
                break;
            }
        }

        // Check to see if the caller is approved to spend shares.
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Redeem shares.
        _burn(owner, shares);

        // Determine the total amount of assets withdrawn.
        assets -= leftToWithdraw;

        // Convert assets decimals back for transfers.
        assets = assets.changeDecimals(decimals, assetDecimals);

        // Only withdraw from position if holding pool does not contain enough funds.
        _allocateAssets(assets);

        // Transfer assets to receiver from the cellar's holding pool.
        asset.safeTransfer(receiver, assets);

        emit Withdraw(receiver, owner, address(asset), assets, shares);

        // The amount of assets actually withdrawn may be less than assets attempted to withdraw
        // if attempted withdraw amount was less than the withdrawable balance.
        return (assets, shares);
    }

    // ================================== ACCOUNTING OPERATIONS ==================================

    /**
     * @dev The internal functions always use 18 decimals of precision while the public functions use
     *      as many decimals as the current asset (aka they don't change the decimals). This is
     *      because we want the user deposit data the cellar stores to be usable across different
     *      assets regardless of the decimals used. This means the cellar will always perform
     *      calculations and store data with a standard of 18 decimals of precision but will change
     *      the decimals back when transferring assets outside the contract or returning data
     *      through public view functions.
     */

    /**
     * @notice Total amount of active asset entered into the current position.
     * @dev The aTokens' value is pegged to the value of the corresponding asset at a 1:1 ratio. We
     *      can find the amount of assets active in a position simply by taking balance of aTokens
     *      cellar holds.
     */
    function activeAssets() public view returns (uint256) {
        return assetAToken.balanceOf(address(this));
    }

    /**
     * @dev Same as `activeAssets` but forcibly denoted with 18 decimals of precision.
     */
    function _activeAssets() internal view returns (uint256) {
        uint256 assets = assetAToken.balanceOf(address(this));
        return assets.changeDecimals(assetDecimals, decimals);
    }

    /**
     * @notice Total amount of inactive asset in holding.
     */
    function inactiveAssets() public view returns (uint256) {
        return asset.balanceOf(address(this));
    }

    /**
     * @dev Same as `inactiveAssets` but forcibly denoted with 18 decimals of precision.
     */
    function _inactiveAssets() internal view returns (uint256) {
        uint256 assets = asset.balanceOf(address(this));
        return assets.changeDecimals(assetDecimals, decimals);
    }

    /**
     * @notice Total amount of the asset managed by the cellar.
     */
    function totalAssets() public view returns (uint256) {
        return activeAssets() + inactiveAssets();
    }

    /**
     * @dev Same as `totalAssets` but forcibly denoted with 18 decimals of precision.
     */
    function _totalAssets() internal view returns (uint256) {
        return _activeAssets() + _inactiveAssets();
    }

    /**
     * @notice The amount of shares that the cellar would exchange for the amount of assets provided
     *         ASSUMING they are active.
     * @param assets amount of assets to convert
     * @return shares the assets can be exchanged for
     */
    function convertToShares(uint256 assets) public view returns (uint256) {
        assets = assets.changeDecimals(assetDecimals, decimals);
        return _convertToShares(assets);
    }

    /**
     * @dev Same as `convertToShares` but forcibly denoted with 18 decimals of precision.
     */
    function _convertToShares(uint256 assets) internal view returns (uint256) {
        uint256 currentTotalAssets =  _totalAssets();
        uint256 currentTotalSupply = totalSupply;
        return currentTotalAssets == 0 || currentTotalSupply == 0 ?
            assets :
            assets.mulDivDown(currentTotalSupply, currentTotalAssets);
    }

    /**
     * @notice The amount of assets that the cellar would exchange for the amount of shares provided
     *         ASSUMING they are active.
     * @param shares amount of shares to convert
     * @return assets the shares can be exchanged for
     */
    function convertToAssets(uint256 shares) public view returns (uint256) {
        uint256 assets = _convertToAssets(shares);
        return assets.changeDecimals(decimals, assetDecimals);
    }

    /**
     * @dev Same as `convertToAssets` but forcibly denoted with 18 decimals of precision.
     */
    function _convertToAssets(uint256 shares) internal view returns (uint256) {
        return totalSupply == 0 ? shares : shares.mulDivDown(_totalAssets(), totalSupply);
    }

    /**
    * @notice Simulate the effects of depositing assets at the current block, given current on-chain
    *         conditions.
     * @param assets amount of assets to deposit
     * @return shares that will be minted
     */
    function previewDeposit(uint256 assets) public view returns (uint256) {
        return convertToShares(assets);
    }

    /**
    * @notice Simulate the effects of minting shares at the current block, given current on-chain
    *         conditions.
     * @param shares amount of shares to mint
     * @return assets that will be deposited
     */
    function previewMint(uint256 shares) public view returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        uint256 assets = supply == 0 ? shares : shares.mulDivUp(_totalAssets(), supply);
        return assets.changeDecimals(decimals, assetDecimals);
    }

    /**
    * @notice Simulate the effects of withdrawing assets at the current block, given current
    *         on-chain conditions ASSUMING the shares being redeemed are all active.
     * @param assets amount of assets to withdraw
     * @return shares that will be redeemed
     */
    function previewWithdraw(uint256 assets) public view returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    /**
    * @notice Simulate the effects of redeeming shares at the current block, given current on-chain
    *         conditions ASSUMING the shares being redeemed are all active.
     * @param shares amount of sharers to redeem
     * @return assets that can be withdrawn
     */
    function previewRedeem(uint256 shares) public view returns (uint256) {
        return convertToAssets(shares);
    }

    // ======================================= STATE INFORMATION =====================================

    /**
     * @notice Retrieve information on a user's deposit balances.
     * @param user address of the user
     * @return userActiveShares amount of active shares the user has
     * @return userInactiveShares amount of inactive shares the user has
     * @return userActiveAssets amount of active assets the user has
     * @return userInactiveAssets amount of inactive assets the user has
     */
    function getUserBalances(address user) external view returns (
        uint256 userActiveShares,
        uint256 userInactiveShares,
        uint256 userActiveAssets,
        uint256 userInactiveAssets
    ) {
        // Retrieve the user's deposits to begin looping through them, generally from oldest to
        // newest deposits. This may not be the case though if shares have been transferred to the
        // user, which will be added to the end of the user's deposits regardless of time
        // deposited.
        UserDeposit[] storage deposits = userDeposits[user];

        // Saves gas by avoiding calling `_convertToAssets` on active shares during each loop.
        uint256 exchangeRate = _convertToAssets(1e18);

        for (uint256 i = currentDepositIndex[user]; i < deposits.length; i++) {
            UserDeposit storage d = deposits[i];

            // Determine whether or not deposit is active or inactive.
            if (d.timeDeposited < lastTimeEnteredPosition) {
                // Saves an extra SLOAD if active and cast type to uint256.
                uint256 dShares = d.shares;

                userActiveShares += dShares;
                userActiveAssets += dShares.mulWadDown(exchangeRate); // Convert active shares to assets.
            } else {
                userInactiveShares += d.shares;
                userInactiveAssets += d.assets;
            }
        }

        // Return assets in their original units.
        userActiveAssets = userActiveAssets.changeDecimals(decimals, assetDecimals);
        userInactiveAssets = userInactiveAssets.changeDecimals(decimals, assetDecimals);
    }

    /**
     * @notice Retrieve a list of all of a user's deposits.
     * @dev This is provided because Solidity converts public arrays into index getters,
     *      but we need a way to allow external contracts and users to access the whole array.
     * @param user address of the user
     * @return array of all the users deposits
     */
    function getUserDeposits(address user) external view returns (UserDeposit[] memory) {
        return userDeposits[user];
    }

    // =========================== DEPOSIT/WITHDRAWAL LIMIT OPERATIONS ===========================

    /**
     * @notice Total number of assets that can be deposited by owner into the cellar.
     * @param owner address of account that would receive the shares
     * @return maximum amount of assets that can be deposited
     */
    function maxDeposit(address owner) public view returns (uint256) {
        if (isShutdown) return 0;

        if (depositLimit == type(uint256).max && liquidityLimit == type(uint256).max)
            // Conversion to fixed point will overflow if the number being converted has more integer
            // digits that fit in the bits reserved for them in the fixed point representation. This
            // is the maximum assets that can be deposited without overflowing.
            return uint256(type(uint112).max) / 10**(decimals - assetDecimals);

        uint256 leftUntilDepositLimit = depositLimit.subMin0(maxWithdraw(owner));
        uint256 leftUntilLiquidityLimit = liquidityLimit.subMin0(totalAssets());

        // Only return the more relevant of the two.
        return MathUtils.min(leftUntilDepositLimit, leftUntilLiquidityLimit);
    }

    /**
     * @notice Total number of shares that can be minted for owner from the cellar.
     * @param owner address of account that would receive the shares
     * @return maximum amount of shares that can be minted
     */
    function maxMint(address owner) public view returns (uint256) {
        return convertToShares(maxDeposit(owner));
    }

    /**
     * @notice Total number of assets that can be withdrawn from the cellar.
     * @param owner address of account that would holds the shares
     * @return maximum amount of assets that can be withdrawn
     */
    function maxWithdraw(address owner) public view returns (uint256) {
        UserDeposit[] storage deposits = userDeposits[owner];

        // Track max assets that can be withdrawn.
        uint256 assets;

        // Saves gas by avoiding calling `_convertToAssets` on active shares during each loop.
        uint256 exchangeRate = _convertToAssets(1e18);

        for (uint256 i = currentDepositIndex[owner]; i < deposits.length; i++) {
            UserDeposit storage d = deposits[i];

            // Determine the amount of assets that can be withdrawn. Only redeem active shares for
            // assets, otherwise just withdrawn the original amount of assets that were deposited.
            assets += d.timeDeposited < lastTimeEnteredPosition ?
                uint256(d.shares).mulWadDown(exchangeRate) :
                d.assets;
        }

        // Converts back to decimals used by that asset.
        return assets.changeDecimals(decimals, assetDecimals);
    }

    /**
     * @notice Total number of shares that can be redeemed from the cellar.
     * @param owner address of account that would holds the shares
     * @return maximum amount of shares that can be redeemed
     */
    function maxRedeem(address owner) public view returns (uint256) {
        return balanceOf[owner];
    }

    // ====================================== FEE OPERATIONS ======================================

    /**
     * @notice Take platform fees and performance fees off of cellar's active assets.
     */
    function accrueFees() external updateYield {
        // Platform fees taken each accrual = activeAssets * (elapsedTime * (feePercentage / SECS_PER_YEAR)).
        uint256 elapsedTime = block.timestamp - fees.lastTimeAccruedPlatformFees;
        uint256 platformFeeInAssets = (_activeAssets() * elapsedTime * PLATFORM_FEE) / DENOMINATOR / 365 days;
        uint256 platformFees = _convertToShares(platformFeeInAssets);

        // Update tracking of last time platform fees were accrued.
        fees.lastTimeAccruedPlatformFees = uint32(block.timestamp);

        // Mint the cellar accrued platform fees as shares.
        _mint(address(this), platformFees);

        // Performance fees taken each accrual = yield * feePercentage
        uint256 yield = fees.yield;
        uint256 performanceFeeInAssets = yield.mulDivDown(PERFORMANCE_FEE, DENOMINATOR);
        uint256 performanceFees = _convertToShares(performanceFeeInAssets);

        // Reset tracking of yield since last accrual.
        fees.yield = 0;

        // Mint the cellar accrued performance fees as shares.
        _mint(address(this), performanceFees);

        // Update fees that have been accrued.
        fees.accruedPlatformFees += uint112(platformFees);
        fees.accruedPerformanceFees += uint112(performanceFees);

        emit AccruedPlatformFees(platformFees);
        emit AccruedPerformanceFees(performanceFees);
    }

    /**
     * @notice Tracks yield the cellar has gained since the last time fees were accrued.
     * @dev Must be called every time a function is called that updates `activeAssets`.
     */
    modifier updateYield() {
        uint256 currentActiveAssets = _activeAssets();
        uint256 lastActiveAssets = fees.lastActiveAssets;

        if (currentActiveAssets > lastActiveAssets) {
            fees.yield += uint112(currentActiveAssets - lastActiveAssets);
        }

        _;

        // Update this for next performance fee accrual.
        fees.lastActiveAssets = uint112(_activeAssets());
    }

    /**
     * @notice Transfer accrued fees to the Sommelier Chain to distribute.
     */
    function transferFees() external onlyOwner {
        // Cellar fees are accrued in shares and redeemed upon transfer.
        uint256 totalFees = ERC20(this).balanceOf(address(this));
        uint256 feeInAssets = previewRedeem(totalFees);

        // Redeem our fee shares for assets to transfer to Cosmos.
        _burn(address(this), totalFees);

        // Only withdraw assets from position if the holding pool does not contain enough funds.
        // Otherwise, all assets will come from the holding pool.
        _allocateAssets(feeInAssets);

        // Transfer assets to a fee distributor on the Sommelier Chain.
        asset.safeApprove(address(gravityBridge), feeInAssets);
        gravityBridge.sendToCosmos(address(asset), feesDistributor, feeInAssets);

        emit TransferFees(fees.accruedPlatformFees, fees.accruedPerformanceFees);

        // Reset the tracker for fees accrued that are still waiting to be transferred.
        fees.accruedPlatformFees = 0;
        fees.accruedPerformanceFees = 0;
    }

    // =================================== GOVERNANCE OPERATIONS ===================================

    /**
     * @notice Trust or distrust an asset position on Aave (eg. FRAX, UST, FEI).
     */
    function setTrust(address position, bool trust) external onlyOwner {
        isTrusted[position] = trust;

        // In the case that governance no longer trust the current position, pull all assets back into
        // the cellar.
        if (trust == false && position == address(asset)) _withdrawFromAave(address(asset), type(uint256).max);
    }

    /**
     * @notice Stop or start the contract. Used in an emergency or if the cellar has been retired.
     */
    function setShutdown(bool shutdown, bool exitPosition) external onlyOwner {
        isShutdown = shutdown;

        // Withdraw everything from the current position on Aave if specified when shutting down.
        if (shutdown && exitPosition) _withdrawFromAave(address(asset), type(uint256).max);

        emit Shutdown(shutdown, exitPosition);
    }

    /**
     * @notice Update the address of the fee distributor on the Sommelier Chain. IMPORTANT: Ensure
     *         that the address is formatted in the specific way that the Gravity contract expects
     *         it to be.
     */
    function setFeesDistributor(bytes32 newFeesDistributor) external onlyOwner {
        // Store for emitted event.
        bytes32 oldFeesDistributor = feesDistributor;

        // Change the fees distributor address.
        feesDistributor = newFeesDistributor;

        emit FeesDistributorChanged(oldFeesDistributor, newFeesDistributor);
    }

    // ===================================== ADMIN OPERATIONS =====================================

    /**
     * @notice Enters into the current Aave stablecoin position.
     */
    function enterPosition() external onlyOwner {
        if (isShutdown) revert STATE_ContractShutdown();

        uint256 currentInactiveAssets = inactiveAssets();

        // Deposits all inactive assets into the current position.
        _depositToAave(address(asset), currentInactiveAssets);

        // Update the last time cellar entered position.
        lastTimeEnteredPosition = block.timestamp;

        emit EnterPosition(address(asset), currentInactiveAssets);
    }

    /**
     * @notice Rebalances current assets into a new asset position.
     * @param route array of [initial token, pool, token, pool, token, ...] that specifies the swap route
     * @param swapParams multidimensional array of [i, j, swap type] where i and j are the correct
                         values for the n'th pool in `_route` and swap type should be 1 for a
                         stableswap `exchange`, 2 for stableswap `exchange_underlying`, 3 for a
                         cryptoswap `exchange`, 4 for a cryptoswap `exchange_underlying` and 5 for
                         Polygon factory metapools `exchange_underlying`
     * @param minAssetsOut minimum amount of assets received from swap
     */
    function rebalance(
        address[9] memory route,
        uint256[3][4] memory swapParams,
        uint256 minAssetsOut
    ) external onlyOwner {
        if (isShutdown) revert STATE_ContractShutdown();

        // Retrieve the last token in the route and store it as the new asset.
        address newAsset;
        for (uint256 i; ; i += 2) {
            if (i == 8 || route[i+1] == address(0)) {
                newAsset = route[i];
                break;
            }
        }

        // Doesn't make sense to rebalance into the same asset.
        if (newAsset == address(asset)) revert USR_SameAsset(newAsset);

        // Pull all active assets entered into Aave back into the cellar so we can swap everything
        // into the new asset.
        _withdrawFromAave(address(asset), type(uint256).max);

        uint256 currentInactiveAssets = inactiveAssets();

        // Perform stablecoin swap using Curve.
        asset.safeApprove(address(curveRegistryExchange), currentInactiveAssets);
        uint256 amountOut = curveRegistryExchange.exchange_multiple(
            route,
            swapParams,
            currentInactiveAssets,
            minAssetsOut
        );

        // Store this later for the event we will emit.
        address oldAsset = address(asset);

        // Updates state for our new position and check to make sure Aave supports it before
        // rebalancing.
        _updatePosition(newAsset);

        // Deposit all newly swapped assets into Aave.
        _depositToAave(address(asset), amountOut);

        // Update the last time all inactive assets were entered into a position.
        lastTimeEnteredPosition = block.timestamp;

        emit Rebalance(oldAsset, newAsset, amountOut);
    }

    /**
     * @notice Reinvest rewards back into cellar's current position.
     * @dev Must be called within 2 day unstake period 10 days after `claimAndUnstake` was run.
     * @param minAssetsOut minimum amount of assets received after swapping AAVE to the current asset
     */
    function reinvest(uint256 minAssetsOut) external onlyOwner {
        // Redeems the cellar's stkAAVE rewards for AAVE.
        stkAAVE.redeem(address(this), type(uint256).max);

        uint256 amountIn = AAVE.balanceOf(address(this));

        // Specify the swap path from AAVE -> WETH -> current asset.
        address[] memory path = new address[](3);
        path[0] = address(AAVE);
        path[1] = address(WETH);
        path[2] = address(asset);

        // Perform a multihop swap using Sushiswap.
        AAVE.safeApprove(address(sushiswapRouter), amountIn);
        uint256[] memory amounts = sushiswapRouter.swapExactTokensForTokens(
            amountIn,
            minAssetsOut,
            path,
            address(this),
            block.timestamp + 60
        );

        uint256 amountOut = amounts[amounts.length - 1];

        // Count reinvested rewards as yield.
        fees.yield += uint112(amountOut.changeDecimals(assetDecimals, decimals));

        // In the case of a shutdown, we just may want to redeem any leftover rewards for users to
        // claim but without entering them back into a position in case the position has been exited.
        if (!isShutdown) _depositToAave(address(asset), amountOut);

        emit Reinvest(address(asset), amountIn, amountOut);
    }

    /**
     * @notice Claim rewards from Aave and begin cooldown period to unstake them.
     * @return claimed amount of rewards claimed from Aave
     */
    function claimAndUnstake() external onlyOwner returns (uint256 claimed) {
        // Necessary to do as `claimRewards` accepts a dynamic array as first param.
        address[] memory aToken = new address[](1);
        aToken[0] = address(assetAToken);

        // Claim all stkAAVE rewards.
        claimed = incentivesController.claimRewards(aToken, type(uint256).max, address(this));

        // Begin the cooldown period for unstaking stkAAVE to later redeem for AAVE.
        stkAAVE.cooldown();

        emit ClaimAndUnstake(claimed);
    }

    /**
     * @notice Sweep tokens sent here that are not managed by the cellar.
     * @dev This may be used in case the wrong tokens are accidentally sent to this contract.
     * @param token address of token to transfer out of this cellar
     * @param to address to transfer sweeped tokens to
     */
    function sweep(address token, address to) external onlyOwner {
        // Prevent sweeping of assets managed by the cellar and shares minted to the cellar as fees.
        if (token == address(asset) || token == address(assetAToken) || token == address(this))
            revert USR_ProtectedAsset(token);

        // Transfer out tokens in this cellar that shouldn't be here.
        uint256 amount = ERC20(token).balanceOf(address(this));
        ERC20(token).safeTransfer(to, amount);

        emit Sweep(token, to, amount);
    }

    /**
     * @notice Sets the maximum liquidity that cellar can manage. Careful to use the same decimals as the
     *         current asset.
     */
    function setLiquidityLimit(uint256 limit) external onlyOwner {
        // Store for emitted event.
        uint256 oldLimit = liquidityLimit;

        // Change the liquidity limit.
        liquidityLimit = limit;

        emit LiquidityLimitChanged(oldLimit, limit);
    }

    /**
     * @notice Sets the per-wallet deposit limit. Careful to use the same decimals as the current asset.
     */
    function setDepositLimit(uint256 limit) external onlyOwner {
        // Store for emitted event.
        uint256 oldLimit = depositLimit;

        // Change the deposit limit.
        depositLimit = limit;

        emit DepositLimitChanged(oldLimit, limit);
    }

    // ========================================== HELPERS ==========================================

    /**
     * @notice Update state variables related to the current position.
     * @dev Be aware that when updating to an asset that uses less decimals than the previous
     *      asset (eg. DAI -> USDC), `depositLimit` and `liquidityLimit` will lose some precision
     *      due to truncation.
     * @param newAsset address of the new asset being managed by the cellar
     */
    function _updatePosition(address newAsset) internal {
        // Retrieve the aToken that will represent the cellar's new position on Aave.
        (, , , , , , , address aTokenAddress, , , , ) = lendingPool.getReserveData(newAsset);

        // If the address is not null, it is supported by Aave.
        if (aTokenAddress == address(0)) revert USR_UnsupportedPosition(newAsset);

        // Update the decimals used by limits if necessary.
        uint8 oldAssetDecimals = assetDecimals;
        uint8 newAssetDecimals = ERC20(newAsset).decimals();

        // Ensure the decimals of precision the new position uses will not break the cellar.
        if (newAssetDecimals > decimals) revert USR_TooManyDecimals(newAssetDecimals, decimals);

        // Ignore if decimals are the same or if it is the first time initializing a position.
        if (oldAssetDecimals != 0 && oldAssetDecimals != newAssetDecimals) {
            if (depositLimit != type(uint256).max) {
                depositLimit = depositLimit.changeDecimals(oldAssetDecimals, newAssetDecimals);
            }

            if (liquidityLimit != type(uint256).max) {
                liquidityLimit = liquidityLimit.changeDecimals(oldAssetDecimals, newAssetDecimals);
            }
        }

        // Update state related to the current position.
        asset = ERC20(newAsset);
        assetDecimals = newAssetDecimals;
        assetAToken = ERC20(aTokenAddress);
    }

    /**
     * @notice Ensures there is enough assets in the contract available for a transfer.
     * @dev Only withdraws from the current position if necessary.
     * @param assets The amount of assets to allocate
     */
    function _allocateAssets(uint256 assets) internal {
        uint256 currentInactiveAssets = inactiveAssets();

        // Only withdraw if not enough assets in the holding pool.
        if (assets > currentInactiveAssets) _withdrawFromAave(address(asset), assets - currentInactiveAssets);
    }

    /**
     * @notice Deposits cellar holdings into an Aave lending pool.
     * @param position the address of the asset position
     * @param assets the amount of assets to deposit
     */
    function _depositToAave(address position, uint256 assets) internal updateYield {
        // Ensure the position has been trusted by governance.
        if (!isTrusted[position]) revert USR_UntrustedPosition(position);

        // Initialize starting point for first platform fee accrual to time when cellar first deposits
        // assets into a position on Aave.
        if (fees.lastTimeAccruedPlatformFees == 0) fees.lastTimeAccruedPlatformFees = uint32(block.timestamp);

        // Deposit assets into Aave position.
        ERC20(position).safeApprove(address(lendingPool), assets);
        lendingPool.deposit(position, assets, address(this), 0);

        emit DepositToAave(position, assets);
    }

    /**
     * @notice Withdraws assets from Aave.
     * @param position the address of the asset position
     * @param assets the amount of assets to withdraw
     */
    function _withdrawFromAave(address position, uint256 assets) internal updateYield {
        // Skip withdrawal instead of reverting if there are no active assets to withdraw. Reverting
        // could potentially prevent important function calls from executing, such as `shutdown`, in
        // the case where there were no active assets because Aave would throw an error.
        if (activeAssets() > 0) {
            // Withdraw assets from Aave position.
            uint256 withdrawnAmount = lendingPool.withdraw(position, assets, address(this));


            // `withdrawnAmount` may be less than `assets` if cellar tried withdrawing more than
            // it's balance on Aave.
            emit WithdrawFromAave(position, withdrawnAmount);
        }
    }

    // ================================= SHARE TRANSFER OPERATIONS =================================

    /**
     * @dev Modified versions of Solmate's ERC20 transfer and transferFrom functions to work with the
     *      cellar's active vs inactive shares model.
     */

    /**
     * @notice Transfers shares from one account to another.
     * @param from address that is sending shares
     * @param to address that is receiving shares
     * @param amount amount of shares to transfer
     * @param onlyActive whether to only transfer active shares
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount,
        bool onlyActive
    ) public returns (bool) {
        // If the sender is not the owner of the shares, check to see if the owner has approved them
        // to spend their shares.
        if (from != msg.sender) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
        }

        // Will revert here if sender is trying to transfer more shares then they have, so no need
        // for an explicit check.
        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        // Retrieve the deposits from sender then begin looping through deposits, generally from
        // oldest to newest deposits. This may not be the case though if shares have been
        // transferred to the sender, as they will be added to the end of the sender's deposits
        // regardless of time deposited.
        UserDeposit[] storage depositsFrom = userDeposits[from];

        // Tracks the amount of shares left to transfer; updated at the end of each loop.
        uint256 leftToTransfer = amount;

        for (uint256 i = currentDepositIndex[from]; i < depositsFrom.length; i++) {
            UserDeposit storage dFrom = depositsFrom[i];

            // If we only want to transfer active shares, skips this deposit if it is inactive.
            bool isActive = dFrom.timeDeposited < lastTimeEnteredPosition;
            if (onlyActive && !isActive) continue;

            // Saves an extra SLOAD if active and cast type to uint256.
            uint256 dFromShares = dFrom.shares;

            // Determine the amount of assets and shares to transfer from this deposit.
            uint256 transferredShares = MathUtils.min(leftToTransfer, dFromShares);
            uint256 transferredAssets = uint256(dFrom.assets).mulDivUp(transferredShares, dFromShares);

            // For active shares, deletes the deposit data we don't need anymore for a gas refund.
            if (isActive) {
                delete dFrom.assets;
                delete dFrom.timeDeposited;
            } else {
                dFrom.assets -= uint112(transferredAssets);
            }

            // Taken shares from this deposit to transfer.
            dFrom.shares -= uint112(transferredShares);

            // Transfer new deposit to the end of receiver's list of deposits.
            userDeposits[to].push(UserDeposit({
                assets: isActive ? 0 : uint112(transferredAssets),
                shares: uint112(transferredShares),
                timeDeposited: isActive ? 0 : dFrom.timeDeposited
            }));

            // Update the counter of assets left to transfer.
            leftToTransfer -= transferredShares;

            // Break if not shares left to transfer.
            if (leftToTransfer == 0) {
                // Only store the index for the next non-zero deposit to save gas on looping if
                // inactive deposits weren't skipped.
                if (!onlyActive) currentDepositIndex[from] = dFrom.shares != 0 ? i : i+1;
                break;
            }
        }

        // Will only happen if exhausted through all deposits and did not enough active shares to
        // transfer.
        if (leftToTransfer != 0) revert USR_NotEnoughActiveShares(leftToTransfer, amount);

        emit Transfer(from, to, amount);

        return true;
    }

    /**
     * @dev For compatibility with ERC20 standard.
     */
    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        // Defaults to allowing both active and inactive shares to be transferred.
        return transferFrom(from, to, amount, false);
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        // Defaults to allowing both active and inactive shares to be transferred.
        return transferFrom(msg.sender, to, amount, false);
    }
}

File 2 of 14 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*///////////////////////////////////////////////////////////////
                                  EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*///////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*///////////////////////////////////////////////////////////////
                             EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*///////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*///////////////////////////////////////////////////////////////
                              ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*///////////////////////////////////////////////////////////////
                              EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );

            address recoveredAddress = ecrecover(digest, v, r, s);

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*///////////////////////////////////////////////////////////////
                       INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 3 of 14 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
    /*///////////////////////////////////////////////////////////////
                            ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool callStatus;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            callStatus := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(callStatus, "ETH_TRANSFER_FAILED");
    }

    /*///////////////////////////////////////////////////////////////
                           ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 100 because the calldata length is 4 + 32 * 3.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
    }

    /*///////////////////////////////////////////////////////////////
                         INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
        assembly {
            // Get how many bytes the call returned.
            let returnDataSize := returndatasize()

            // If the call reverted:
            if iszero(callStatus) {
                // Copy the revert message into memory.
                returndatacopy(0, 0, returnDataSize)

                // Revert with the same message.
                revert(0, returnDataSize)
            }

            switch returnDataSize
            case 32 {
                // Copy the return data into memory.
                returndatacopy(0, 0, returnDataSize)

                // Set success to whether it returned true.
                success := iszero(iszero(mload(0)))
            }
            case 0 {
                // There was no return data.
                success := 1
            }
            default {
                // It returned some malformed input.
                success := 0
            }
        }
    }
}

File 4 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 5 of 14 : IAaveIncentivesController.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

interface IAaveIncentivesController {
    event RewardsAccrued(address indexed user, uint256 amount);

    event RewardsClaimed(address indexed user, address indexed to, address indexed claimer, uint256 amount);

    event ClaimerSet(address indexed user, address indexed claimer);

    /*
     * @dev Returns the configuration of the distribution for a certain asset
     * @param asset The address of the reference asset of the distribution
     * @return The asset index, the emission per second and the last updated timestamp
     **/
    function getAssetData(address asset)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );

    /*
     * LEGACY **************************
     * @dev Returns the configuration of the distribution for a certain asset
     * @param asset The address of the reference asset of the distribution
     * @return The asset index, the emission per second and the last updated timestamp
     **/
    function assets(address asset)
        external
        view
        returns (
            uint128,
            uint128,
            uint256
        );

    /**
     * @dev Whitelists an address to claim the rewards on behalf of another address
     * @param user The address of the user
     * @param claimer The address of the claimer
     */
    function setClaimer(address user, address claimer) external;

    /**
     * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
     * @param user The address of the user
     * @return The claimer address
     */
    function getClaimer(address user) external view returns (address);

    /**
     * @dev Configure assets for a certain rewards emission
     * @param assets The assets to incentivize
     * @param emissionsPerSecond The emission for each asset
     */
    function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond) external;

    /**
     * @dev Called by the corresponding asset on any update that affects the rewards distribution
     * @param asset The address of the user
     * @param userBalance The balance of the user of the asset in the lending pool
     * @param totalSupply The total supply of the asset in the lending pool
     **/
    function handleAction(
        address asset,
        uint256 userBalance,
        uint256 totalSupply
    ) external;

    /**
     * @dev Returns the total of rewards of an user, already accrued + not yet accrued
     * @param user The address of the user
     * @return The rewards
     **/
    function getRewardsBalance(address[] calldata assets, address user) external view returns (uint256);

    /**
     * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards
     * @param amount Amount of rewards to claim
     * @param to Address that will be receiving the rewards
     * @return Rewards claimed
     **/
    function claimRewards(
        address[] calldata assets,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards. The caller must
     * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
     * @param amount Amount of rewards to claim
     * @param user Address to check and claim rewards
     * @param to Address that will be receiving the rewards
     * @return Rewards claimed
     **/
    function claimRewardsOnBehalf(
        address[] calldata assets,
        uint256 amount,
        address user,
        address to
    ) external returns (uint256);

    /**
     * @dev returns the unclaimed rewards of the user
     * @param user the address of the user
     * @return the unclaimed user rewards
     */
    function getUserUnclaimedRewards(address user) external view returns (uint256);

    /**
     * @dev returns the unclaimed rewards of the user
     * @param user the address of the user
     * @param asset The asset to incentivize
     * @return the user index for the asset
     */
    function getUserAssetData(address user, address asset) external view returns (uint256);

    /**
     * @dev for backward compatibility with previous implementation of the Incentives controller
     */
    function REWARD_TOKEN() external view returns (address);

    /**
     * @dev for backward compatibility with previous implementation of the Incentives controller
     */
    function PRECISION() external view returns (uint8);

    /**
     * @dev Gets the distribution end timestamp of the emissions
     */
    function DISTRIBUTION_END() external view returns (uint256);
}

File 6 of 14 : IStakedTokenV2.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.11;

interface IStakedTokenV2 {
    function stake(address to, uint256 amount) external;

    function redeem(address to, uint256 amount) external;

    function cooldown() external;

    function claimRewards(address to, uint256 amount) external;

    function balanceOf(address account) external view returns (uint256);

    function stakersCooldowns(address account) external view returns (uint256);
}

File 7 of 14 : ICurveSwaps.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

/**
 * @notice Partial interface for a Curve Registry Exchanges contract
 * @dev The registry exchange contract is used to find pools and query exchange rates for token swaps.
 *      It also provides a unified exchange API that can be useful for on-chain integrators.
 **/
interface ICurveSwaps {
    /**
     * @notice Perform up to four swaps in a single transaction
     * @dev Routing and swap params must be determined off-chain. This
     *      functionality is designed for gas efficiency over ease-of-use.
     * @param _route Array of [initial token, pool, token, pool, token, ...]
     *               The array is iterated until a pool address of 0x00, then the last
     *               given token is transferred to `_receiver` (address to transfer the final output token to)
     * @param _swap_params Multidimensional array of [i, j, swap type] where i and j are the correct
     *                     values for the n'th pool in `_route`. The swap type should be 1 for
     *                     a stableswap `exchange`, 2 for stableswap `exchange_underlying`, 3
     *                     for a cryptoswap `exchange`, 4 for a cryptoswap `exchange_underlying`
     *                     and 5 for Polygon factory metapools `exchange_underlying`
     * @param _expected The minimum amount received after the final swap.
     * @return Received amount of final output token
     **/
    function exchange_multiple(
        address[9] memory _route,
        uint256[3][4] memory _swap_params,
        uint256 _amount,
        uint256 _expected
    ) external returns (uint256);
}

File 8 of 14 : ISushiSwapRouter.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

/**
 * @notice Partial interface for a SushiSwap Router contract
 **/
interface ISushiSwapRouter {
    /**
     * @notice Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the `path`
     * @dev The first element of `path` is the input token, the last is the output token,
     *      and any intermediate elements represent intermediate pairs to trade through (if, for example, a direct pair does not exist).
     *      `msg.sender` should have already given the router an allowance of at least `amountIn` on the input token
     * @param amountIn The amount of input tokens to send
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert
     * @param path An array of token addresses. `path.length` must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity
     * @param to Recipient of the output tokens
     * @param deadline Unix timestamp after which the transaction will revert
     * @return amounts The input token amount and all subsequent output token amounts
     **/
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

File 9 of 14 : IGravity.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

interface IGravity {
    function sendToCosmos(
        address _tokenContract,
        bytes32 _destination,
        uint256 _amount
    ) external;
}

File 10 of 14 : ILendingPool.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

/**
 * @dev Partial interface for a Aave LendingPool contract,
 * which is the main point of interaction with an Aave protocol's market
 **/
interface ILendingPool {
    /**
     * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to deposit
     * @param amount The amount to be deposited
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode
    ) external;

    /**
     * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to Address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(
        address asset,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve's normalized income
     */
    function getReserveNormalizedIncome(address asset) external view returns (uint256);

    /**
     * @dev Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     **/
    function getReserveData(address asset)
        external
        view
        returns (
            //stores the reserve configuration
            //bit 0-15: LTV
            //bit 16-31: Liq. threshold
            //bit 32-47: Liq. bonus
            //bit 48-55: Decimals
            //bit 56: Reserve is active
            //bit 57: reserve is frozen
            //bit 58: borrowing is enabled
            //bit 59: stable rate borrowing enabled
            //bit 60-63: reserved
            //bit 64-79: reserve factor
            uint256 configuration,
            //the liquidity index. Expressed in ray
            uint128 liquidityIndex,
            //variable borrow index. Expressed in ray
            uint128 variableBorrowIndex,
            //the current supply rate. Expressed in ray
            uint128 currentLiquidityRate,
            //the current variable borrow rate. Expressed in ray
            uint128 currentVariableBorrowRate,
            //the current stable borrow rate. Expressed in ray
            uint128 currentStableBorrowRate,
            uint40 lastUpdateTimestamp,
            //tokens addresses
            address aTokenAddress,
            address stableDebtTokenAddress,
            address variableDebtTokenAddress,
            //address of the interest rate strategy
            address interestRateStrategyAddress,
            //the id of the reserve. Represents the position in the list of the active reserves
            uint8 id
        );
}

File 11 of 14 : MathUtils.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

library MathUtils {
    /**
     * @notice Substract and return 0 instead if results are negative.
     */
    function subMin0(uint256 x, uint256 y) internal pure returns (uint256) {
        return x > y ? x - y : 0;
    }

    /**
     * @notice Used to change the decimals of precision used for an amount.
     */
    function changeDecimals(
        uint256 amount,
        uint8 fromDecimals,
        uint8 toDecimals
    ) internal pure returns (uint256) {
        if (fromDecimals == toDecimals) {
            return amount;
        } else if (fromDecimals < toDecimals) {
            return amount * 10**(toDecimals - fromDecimals);
        } else {
            return ceilDiv(amount, 10**(fromDecimals - toDecimals));
        }
    }

    // ===================================== OPENZEPPELIN'S MATH =====================================

    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    // ================================= SOLMATE's FIXEDPOINTMATHLIB =================================

    uint256 public constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }
}

File 12 of 14 : Errors.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

// ========================================== USER ERRORS ===========================================

/**
 * @dev These errors represent invalid user input to functions. Where appropriate, the invalid value
 *      is specified along with constraints. These errors can be resolved by callers updating their
 *      arguments.
 */

/**
 * @notice Attempted an action with zero assets.
 */
error USR_ZeroAssets();

/**
 * @notice Attempted an action with zero shares.
 */
error USR_ZeroShares();

/**
 * @notice Attempted deposit more than the max deposit.
 * @param assets the assets user attempted to deposit
 * @param maxDeposit the max assets that can be deposited
 */
error USR_DepositRestricted(uint256 assets, uint256 maxDeposit);

/**
 * @notice Attempted to transfer more active shares than the user has.
 * @param activeShares amount of shares user has
 * @param attemptedActiveShares amount of shares user tried to transfer
 */
error USR_NotEnoughActiveShares(uint256 activeShares, uint256 attemptedActiveShares);

/**
 * @notice Attempted swap into an asset that is not the current asset of the cellar.
 * @param assetOut address of the asset attempted to swap to
 * @param currentAsset address of the current asset of cellar
 */
error USR_InvalidSwap(address assetOut, address currentAsset);

/**
 * @notice Attempted to sweep an asset that is managed by the cellar.
 * @param token address of the token that can't be sweeped
 */
error USR_ProtectedAsset(address token);

/**
 * @notice Attempted rebalance into the same asset.
 * @param asset address of the asset
 */
error USR_SameAsset(address asset);

/**
 * @notice Attempted to update the position to one that is not supported by the platform.
 * @param unsupportedPosition address of the unsupported position
 */
error USR_UnsupportedPosition(address unsupportedPosition);

/**
 * @notice Attempted rebalance into an untrusted position.
 * @param asset address of the asset
 */
error USR_UntrustedPosition(address asset);

/**
 * @notice Attempted to update a position to an asset that uses an incompatible amount of decimals.
 * @param newDecimals decimals of precision that the new position uses
 * @param maxDecimals maximum decimals of precision for a position to be compatible with the cellar
 */
error USR_TooManyDecimals(uint8 newDecimals, uint8 maxDecimals);

/**
 * @notice User attempted to stake zero amout.
 */
error USR_ZeroDeposit();

/**
 * @notice User attempted to stake an amount smaller than the minimum deposit.
 *
 * @param amount                Amount user attmpted to stake.
 * @param minimumDeposit        The minimum deopsit amount accepted.
 */
error USR_MinimumDeposit(uint256 amount, uint256 minimumDeposit);

/**
 * @notice The specified deposit ID does not exist for the caller.
 *
 * @param depositId             The deposit ID provided for lookup.
 */
error USR_NoDeposit(uint256 depositId);

/**
 * @notice The user is attempting to cancel unbonding for a deposit which is not unbonding.
 *
 * @param depositId             The deposit ID the user attempted to cancel.
 */
error USR_NotUnbonding(uint256 depositId);

/**
 * @notice The user is attempting to unbond a deposit which has already been unbonded.
 *
 * @param depositId             The deposit ID the user attempted to unbond.
 */
error USR_AlreadyUnbonding(uint256 depositId);

/**
 * @notice The user is attempting to unstake a deposit which is still timelocked.
 *
 * @param depositId             The deposit ID the user attempted to unstake.
 */
error USR_StakeLocked(uint256 depositId);

/**
 * @notice The contract owner attempted to update rewards but the new reward rate would cause overflow.
 */
error USR_RewardTooLarge();

/**
 * @notice The reward distributor attempted to update rewards but 0 rewards per epoch.
 *         This can also happen if there is less than 1 wei of rewards per second of the
 *         epoch - due to integer division this will also lead to 0 rewards.
 */
error USR_ZeroRewardsPerEpoch();

/**
 * @notice The caller attempted to stake with a lock value that did not
 *         correspond to a valid staking time.
 *
 * @param lock                  The provided lock value.
 */
error USR_InvalidLockValue(uint256 lock);

// ========================================== STATE ERRORS ===========================================

/**
 * @dev These errors represent actions that are being prevented due to current contract state.
 *      These errors do not relate to user input, and may or may not be resolved by other actions
 *      or the progression of time.
 */

/**
 * @notice Attempted action was prevented due to contract being shutdown.
 */
error STATE_ContractShutdown();

/**
 * @notice Attempted to shutdown the contract when it was already shutdown.
 */
error STATE_AlreadyShutdown();

/**
 * @notice The caller attempted to start a reward period, but the contract did not have enough tokens
 *         for the specified amount of rewards.
 *
 * @param rewardBalance         The amount of distributionToken held by the contract.
 * @param reward                The amount of rewards the caller attempted to distribute.
 */
error STATE_RewardsNotFunded(uint256 rewardBalance, uint256 reward);

/**
 * @notice The caller attempted to change the epoch length, but current reward epochs were active.
 */
error STATE_RewardsOngoing();

/**
 * @notice The caller attempted to deposit stake, but there are no remaining rewards to pay out.
 */
error STATE_NoRewardsLeft();

/**
 * @notice The caller attempted to perform an an emergency unstake, but the contract
 *         is not in emergency mode.
 */
error STATE_NoEmergencyUnstake();

/**
 * @notice The caller attempted to perform an an emergency unstake, but the contract
 *         is not in emergency mode, or the emergency mode does not allow claiming rewards.
 */
error STATE_NoEmergencyClaim();

/**
 * @notice The caller attempted to perform a state-mutating action (e.g. staking or unstaking)
 *         while the contract was paused.
 */
error STATE_ContractPaused();

/**
 * @notice The caller attempted to perform a state-mutating action (e.g. staking or unstaking)
 *         while the contract was killed (placed in emergency mode).
 * @dev    Emergency mode is irreversible.
 */
error STATE_ContractKilled();

File 13 of 14 : IAaveV2StablecoinCellar.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.11;

/// @title interface for AaveV2StablecoinCellar
interface IAaveV2StablecoinCellar {
    // ======================================= EVENTS =======================================

    /**
     * @notice Emitted when assets are deposited into cellar.
     * @param caller the address of the caller
     * @param token the address of token the cellar receives
     * @param owner the address of the owner of shares
     * @param assets the amount of assets being deposited
     * @param shares the amount of shares minted to owner
     */
    event Deposit(address indexed caller, address indexed owner, address indexed token, uint256 assets, uint256 shares);

    /**
     * @notice Emitted when assets are withdrawn from cellar.
     * @param receiver the address of the receiver of the withdrawn assets
     * @param owner the address of the owner of the shares
     * @param token the address of the token withdrawn
     * @param assets the amount of assets being withdrawn
     * @param shares the amount of shares burned from owner
     */
    event Withdraw(
        address indexed receiver,
        address indexed owner,
        address indexed token,
        uint256 assets,
        uint256 shares
    );

    /**
     * @notice Emitted on deposit to Aave.
     * @param position the address of the position
     * @param assets the amount of assets to deposit
     */
    event DepositToAave(address indexed position, uint256 assets);

    /**
     * @notice Emitted on withdraw from Aave.
     * @param position the address of the position
     * @param assets the amount of assets to withdraw
     */
    event WithdrawFromAave(address indexed position, uint256 assets);

    /**
     * @notice Emitted upon entering cellar's inactive assets into the current position on Aave.
     * @param position the address of the asset being entered into the current position
     * @param assets amount of assets being entered
     */
    event EnterPosition(address indexed position, uint256 assets);

    /**
     * @notice Emitted upon claiming rewards and beginning cooldown period to unstake them.
     * @param rewardsClaimed amount of rewards that were claimed
     */
    event ClaimAndUnstake(uint256 rewardsClaimed);

    /**
     * @notice Emitted upon reinvesting rewards into the current position.
     * @param token the address of the asset rewards were swapped to
     * @param rewards amount of rewards swapped to be reinvested
     * @param assets amount of assets received from swapping rewards
     */
    event Reinvest(address indexed token, uint256 rewards, uint256 assets);

    /**
     * @notice Emitted on rebalance of Aave poisition.
     * @param oldAsset the address of the asset for the old position
     * @param newAsset the address of the asset for the new position
     * @param assets the amount of the new assets cellar has after rebalancing
     */
    event Rebalance(address indexed oldAsset, address indexed newAsset, uint256 assets);

    /**
     * @notice Emitted when platform fees accrued.
     * @param feesInShares amount of fees accrued in shares
     */
    event AccruedPlatformFees(uint256 feesInShares);

    /**
     * @notice Emitted when performance fees accrued.
     * @param feesInShares amount of fees accrued in shares
     */
    event AccruedPerformanceFees(uint256 feesInShares);

    /**
     * @notice Emitted when platform fees are transferred to Cosmos.
     * @param platformFees amount of platform fees transferred
     * @param performanceFees amount of performance fees transferred
     */
    event TransferFees(uint112 platformFees, uint112 performanceFees);

    /**
     * @notice Emitted when the liquidity limit is changed.
     * @param oldLimit amount the limit was changed from
     * @param newLimit amount the limit was changed to
     */
    event LiquidityLimitChanged(uint256 oldLimit, uint256 newLimit);

    /**
     * @notice Emitted when the deposit limit is changed.
     * @param oldLimit amount the limit was changed from
     * @param newLimit amount the limit was changed to
     */
    event DepositLimitChanged(uint256 oldLimit, uint256 newLimit);

    /**
     * @notice Emitted when fees distributor is changed.
     * @param oldFeesDistributor address of fee distributor was changed from
     * @param newFeesDistributor address of fee distributor was changed to
     */
    event FeesDistributorChanged(bytes32 oldFeesDistributor, bytes32 newFeesDistributor);

    /**
     * @notice Emitted when tokens accidentally sent to cellar are recovered.
     * @param token the address of the token
     * @param to the address sweeped tokens were transferred to
     * @param amount amount transferred out
     */
    event Sweep(address indexed token, address indexed to, uint256 amount);

    /**
     * @notice Emitted when cellar is shutdown.
     * @param isShutdown whether the contract is shutdown
     * @param exitPosition whether to exit the current position
     */
    event Shutdown(bool isShutdown, bool exitPosition);

    // ======================================= STRUCTS =======================================

    /**
     * @notice Stores user deposit data.
     * @param assets amount of assets deposited
     * @param shares amount of shares that were minted for their deposit
     * @param timeDeposited timestamp of when the user deposited
     */
    struct UserDeposit {
        uint112 assets;
        uint112 shares;
        uint32 timeDeposited;
    }

    /**
     * @notice Stores fee-related data.
     */
    struct Fees {
        /**
         * @notice Amount of yield earned since last time performance fees were accrued.
         */
        uint112 yield;
        /**
         * @notice Amount of active assets in cellar since yield was last calculated.
         */
        uint112 lastActiveAssets;
        /**
         * @notice Timestamp of last time platform fees were accrued.
         */
        uint32 lastTimeAccruedPlatformFees;
        /**
         * @notice Amount of platform fees that have been accrued awaiting transfer.
         * @dev Fees are taken in shares and redeemed for assets at the time they are transferred from
         *      the cellar to Cosmos to be distributed.
         */
        uint112 accruedPlatformFees;
        /**
         * @notice Amount of performance fees that have been accrued awaiting transfer.
         * @dev Fees are taken in shares and redeemed for assets at the time they are transferred from
         *      the cellar to Cosmos to be distributed.
         */
        uint112 accruedPerformanceFees;
    }

    // ================================= DEPOSIT/WITHDRAWAL OPERATIONS =================================

    function deposit(uint256 assets, address receiver) external returns (uint256);

    function mint(uint256 shares, address receiver) external returns (uint256);

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    // ==================================== ACCOUNTING OPERATIONS ====================================

    function activeAssets() external view returns (uint256);

    function inactiveAssets() external view returns (uint256);

    function totalAssets() external view returns (uint256);

    function convertToShares(uint256 assets) external view returns (uint256);

    function convertToAssets(uint256 shares) external view returns (uint256);

    function previewDeposit(uint256 assets) external view returns (uint256);

    function previewMint(uint256 shares) external view returns (uint256);

    function previewWithdraw(uint256 assets) external view returns (uint256);

    function previewRedeem(uint256 shares) external view returns (uint256);

    // ======================================= STATE INFORMATION =====================================

    function getUserBalances(address user)
        external
        view
        returns (
            uint256 userActiveShares,
            uint256 userInactiveShares,
            uint256 userActiveAssets,
            uint256 userInactiveAssets
        );

    function getUserDeposits(address user) external view returns (UserDeposit[] memory);

    // ============================ DEPOSIT/WITHDRAWAL LIMIT OPERATIONS ============================

    function maxDeposit(address owner) external view returns (uint256);

    function maxMint(address owner) external view returns (uint256);

    function maxWithdraw(address owner) external view returns (uint256);

    function maxRedeem(address owner) external view returns (uint256);

    // ======================================= FEE OPERATIONS =======================================

    function accrueFees() external;

    function transferFees() external;

    // ======================================= ADMIN OPERATIONS =======================================

    function enterPosition() external;

    function rebalance(
        address[9] memory route,
        uint256[3][4] memory swapParams,
        uint256 minAmountOut
    ) external;

    function reinvest(uint256 minAmountOut) external;

    function claimAndUnstake() external returns (uint256 claimed);

    function sweep(address token, address to) external;

    function setLiquidityLimit(uint256 limit) external;

    function setDepositLimit(uint256 limit) external;

    function setShutdown(bool shutdown, bool exitPosition) external;

    // ================================== SHARE TRANSFER OPERATIONS ==================================

    function transferFrom(
        address from,
        address to,
        uint256 amount,
        bool onlyActive
    ) external returns (bool);
}

File 14 of 14 : 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;
    }
}

Settings
{
  "metadata": {
    "bytecodeHash": "none"
  },
  "optimizer": {
    "enabled": true,
    "runs": 100,
    "details": {
      "yul": true,
      "yulDetails": {
        "stackAllocation": true
      }
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ERC20","name":"_asset","type":"address"},{"internalType":"address[]","name":"_approvedPositions","type":"address[]"},{"internalType":"contract ICurveSwaps","name":"_curveRegistryExchange","type":"address"},{"internalType":"contract ISushiSwapRouter","name":"_sushiswapRouter","type":"address"},{"internalType":"contract ILendingPool","name":"_lendingPool","type":"address"},{"internalType":"contract IAaveIncentivesController","name":"_incentivesController","type":"address"},{"internalType":"contract IGravity","name":"_gravityBridge","type":"address"},{"internalType":"contract IStakedTokenV2","name":"_stkAAVE","type":"address"},{"internalType":"contract ERC20","name":"_AAVE","type":"address"},{"internalType":"contract ERC20","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"STATE_ContractShutdown","type":"error"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"maxDeposit","type":"uint256"}],"name":"USR_DepositRestricted","type":"error"},{"inputs":[{"internalType":"uint256","name":"activeShares","type":"uint256"},{"internalType":"uint256","name":"attemptedActiveShares","type":"uint256"}],"name":"USR_NotEnoughActiveShares","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"USR_ProtectedAsset","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"USR_SameAsset","type":"error"},{"inputs":[{"internalType":"uint8","name":"newDecimals","type":"uint8"},{"internalType":"uint8","name":"maxDecimals","type":"uint8"}],"name":"USR_TooManyDecimals","type":"error"},{"inputs":[{"internalType":"address","name":"unsupportedPosition","type":"address"}],"name":"USR_UnsupportedPosition","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"USR_UntrustedPosition","type":"error"},{"inputs":[],"name":"USR_ZeroAssets","type":"error"},{"inputs":[],"name":"USR_ZeroShares","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feesInShares","type":"uint256"}],"name":"AccruedPerformanceFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feesInShares","type":"uint256"}],"name":"AccruedPlatformFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardsClaimed","type":"uint256"}],"name":"ClaimAndUnstake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"DepositLimitChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"DepositToAave","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"EnterPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"oldFeesDistributor","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newFeesDistributor","type":"bytes32"}],"name":"FeesDistributorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"LiquidityLimitChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAsset","type":"address"},{"indexed":true,"internalType":"address","name":"newAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"Reinvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isShutdown","type":"bool"},{"indexed":false,"internalType":"bool","name":"exitPosition","type":"bool"}],"name":"Shutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Sweep","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"platformFees","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"performanceFees","type":"uint112"}],"name":"TransferFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"WithdrawFromAave","type":"event"},{"inputs":[],"name":"AAVE","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERFORMANCE_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATFORM_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"activeAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetAToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAndUnstake","outputs":[{"internalType":"uint256","name":"claimed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"currentDepositIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curveRegistryExchange","outputs":[{"internalType":"contract ICurveSwaps","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enterPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fees","outputs":[{"internalType":"uint112","name":"yield","type":"uint112"},{"internalType":"uint112","name":"lastActiveAssets","type":"uint112"},{"internalType":"uint32","name":"lastTimeAccruedPlatformFees","type":"uint32"},{"internalType":"uint112","name":"accruedPlatformFees","type":"uint112"},{"internalType":"uint112","name":"accruedPerformanceFees","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feesDistributor","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserBalances","outputs":[{"internalType":"uint256","name":"userActiveShares","type":"uint256"},{"internalType":"uint256","name":"userInactiveShares","type":"uint256"},{"internalType":"uint256","name":"userActiveAssets","type":"uint256"},{"internalType":"uint256","name":"userInactiveAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserDeposits","outputs":[{"components":[{"internalType":"uint112","name":"assets","type":"uint112"},{"internalType":"uint112","name":"shares","type":"uint112"},{"internalType":"uint32","name":"timeDeposited","type":"uint32"}],"internalType":"struct IAaveV2StablecoinCellar.UserDeposit[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gravityBridge","outputs":[{"internalType":"contract IGravity","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inactiveAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentivesController","outputs":[{"internalType":"contract IAaveIncentivesController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTrusted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimeEnteredPosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lendingPool","outputs":[{"internalType":"contract ILendingPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[9]","name":"route","type":"address[9]"},{"internalType":"uint256[3][4]","name":"swapParams","type":"uint256[3][4]"},{"internalType":"uint256","name":"minAssetsOut","type":"uint256"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minAssetsOut","type":"uint256"}],"name":"reinvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newFeesDistributor","type":"bytes32"}],"name":"setFeesDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"setLiquidityLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"shutdown","type":"bool"},{"internalType":"bool","name":"exitPosition","type":"bool"}],"name":"setShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"position","type":"address"},{"internalType":"bool","name":"trust","type":"bool"}],"name":"setTrust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stkAAVE","outputs":[{"internalType":"contract IStakedTokenV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sushiswapRouter","outputs":[{"internalType":"contract ISushiSwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"onlyActive","type":"bool"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userDeposits","outputs":[{"internalType":"uint112","name":"assets","type":"uint112"},{"internalType":"uint112","name":"shares","type":"uint112"},{"internalType":"uint32","name":"timeDeposited","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

6101e060405273b813554b423266bbd4c16c32fa383394868c1f55600f553480156200002a57600080fd5b506040516200590d3803806200590d8339810160408190526200004d91620007c4565b6040518060600160405280602c8152602001620058e1602c91396040518060400160405280600b81526020016a61617665322d434c522d5360a81b81525060128260009080519060200190620000a5929190620006e0565b508151620000bb906001906020850190620006e0565b5060ff81166080524660a052620000d162000217565b60c05250620000e49150339050620002b3565b6001600160a01b0380891660e05287811661010052868116610120528581166101405284811661016052838116610180528281166101a0528181166101c0528a166000908152600b60205260409020805460ff19166001179055620001498a62000305565b6008546000906200016690600160a01b900460ff16600a62000a5f565b90506200017781624c4b4062000a70565b601055620001888161c35062000a70565b60115560005b8a51811015620001fa576001600b60008d8481518110620001b357620001b362000a92565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905580620001f18162000aa8565b9150506200018e565b5062000206856200054c565b505050505050505050505062000d6b565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516200024b919062000b03565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b610120516040516335ea6a7560e01b81526001600160a01b03838116600483015260009216906335ea6a759060240161018060405180830381865afa15801562000353573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000379919062000bd1565b5050505097505050505050505060006001600160a01b0316816001600160a01b03161415620003cb57604051630a5c5e7d60e11b81526001600160a01b03831660048201526024015b60405180910390fd5b6000600860149054906101000a900460ff1690506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000420573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000446919062000cc8565b905060805160ff168160ff1611156200048457608051604051630651982f60e11b815260ff80841660048301529091166024820152604401620003c2565b60ff8216158015906200049d57508060ff168260ff1614155b15620004ff5760001960115414620004d157620004cd82826011546200061d60201b62002c58179092919060201c565b6011555b60001960105414620004ff57620004fb82826010546200061d60201b62002c58179092919060201c565b6010555b600780546001600160a01b039586166001600160a01b031991821617909155600880549490951660ff909216600160a01b02166001600160a81b0319909316929092179190911790915550565b6006546001600160a01b03163314620005a85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620003c2565b6001600160a01b0381166200060f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620003c2565b6200061a81620002b3565b50565b60008160ff168360ff1614156200063657508262000696565b8160ff168360ff161015620006725762000651838362000ce6565b6200065e90600a62000a5f565b6200066a908562000a70565b905062000696565b6200066a8462000683848662000ce6565b6200069090600a62000a5f565b6200069d565b9392505050565b6000620006ab828462000d22565b15620006b9576001620006bc565b60005b60ff16620006cb838562000d39565b620006d7919062000d50565b90505b92915050565b828054620006ee9062000ac6565b90600052602060002090601f0160209004810192826200071257600085556200075d565b82601f106200072d57805160ff19168380011785556200075d565b828001600101855582156200075d579182015b828111156200075d57825182559160200191906001019062000740565b506200076b9291506200076f565b5090565b5b808211156200076b576000815560010162000770565b6001600160a01b03811681146200061a57600080fd5b8051620007a98162000786565b919050565b634e487b7160e01b600052604160045260246000fd5b6000806000806000806000806000806101408b8d031215620007e557600080fd5b8a51620007f28162000786565b60208c0151909a506001600160401b03808211156200081057600080fd5b818d0191508d601f8301126200082557600080fd5b8151818111156200083a576200083a620007ae565b604051601f19603f8360051b011681018181108482111715620008615762000861620007ae565b6040528181526020808201935060059290921b8401909101908f8211156200088857600080fd5b6020840193505b81841015620008b557620008a3846200079c565b8352602093840193909201916200088f565b9b50620008c99250505060408c016200079c565b9750620008d960608c016200079c565b9650620008e960808c016200079c565b9550620008f960a08c016200079c565b94506200090960c08c016200079c565b93506200091960e08c016200079c565b92506200092a6101008c016200079c565b91506200093b6101208c016200079c565b90509295989b9194979a5092959850565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620009a35781600019048211156200098757620009876200094c565b808516156200099557918102915b93841c939080029062000967565b509250929050565b600082620009bc57506001620006da565b81620009cb57506000620006da565b8160018114620009e45760028114620009ef5762000a0f565b6001915050620006da565b60ff84111562000a035762000a036200094c565b50506001821b620006da565b5060208310610133831016604e8410600b841016171562000a34575081810a620006da565b62000a40838362000962565b806000190482111562000a575762000a576200094c565b029392505050565b6000620006d760ff841683620009ab565b600081600019048311821515161562000a8d5762000a8d6200094c565b500290565b634e487b7160e01b600052603260045260246000fd5b600060001982141562000abf5762000abf6200094c565b5060010190565b600181811c9082168062000adb57607f821691505b6020821081141562000afd57634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c91508083168062000b2057607f831692505b602080841082141562000b4157634e487b7160e01b86526022600452602486fd5b81801562000b58576001811462000b6a5762000b99565b60ff1986168952848901965062000b99565b60008a81526020902060005b8681101562000b915781548b82015290850190830162000b76565b505084890196505b509498975050505050505050565b80516001600160801b0381168114620007a957600080fd5b805160ff81168114620007a957600080fd5b6000806000806000806000806000806000806101808d8f03121562000bf557600080fd5b8c519b5062000c0760208e0162000ba7565b9a5062000c1760408e0162000ba7565b995062000c2760608e0162000ba7565b985062000c3760808e0162000ba7565b975062000c4760a08e0162000ba7565b965060c08d015164ffffffffff8116811462000c6257600080fd5b955062000c7260e08e016200079c565b945062000c836101008e016200079c565b935062000c946101208e016200079c565b925062000ca56101408e016200079c565b915062000cb66101608e0162000bbf565b90509295989b509295989b509295989b565b60006020828403121562000cdb57600080fd5b620006d78262000bbf565b600060ff821660ff84168082101562000d035762000d036200094c565b90039392505050565b634e487b7160e01b600052601260045260246000fd5b60008262000d345762000d3462000d0c565b500690565b60008262000d4b5762000d4b62000d0c565b500490565b6000821982111562000d665762000d666200094c565b500190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c051614a1662000ecb600039600081816108270152611b400152600081816105ba01528181611a4c01528181611aec0152611bbf01526000818161048d015281816119cf0152612145015260008181610764015281816125f0015261264901526000818161084e01526120c101526000818161078b01528181612dcb01528181612f520152818161328c01526132e40152600081816109e901528181611be10152611c120152600081816107f801528181610ddb0152610e1a0152600061102f01526000610fff01526000818161051b01528181610b15015281816113d401528181611cef01528181611fb201528181611fef015281816122280152818161226f01528181612731015281816128d201528181613088015281816130cd015281816134b3015281816136c10152613b440152614a166000f3fe608060405234801561001057600080fd5b506004361061039d5760003560e01c806394bf804d116101eb578063bf86d69011610110578063d905777e116100a8578063d905777e1461097d578063dd62ed3e146109a6578063df05a52a146109d1578063e9240c2d146109e4578063e93f566514610a0b578063ecf7085814610a14578063ef8b30f714610a1d578063f2fde38b14610a30578063f82d4d9b14610a4357600080fd5b8063bf86d690146108e2578063c17f6740146108ef578063c2d4160114610902578063c2fbe7bc14610916578063c63d75b61461091e578063c6e6f59214610931578063cab5923814610944578063ce96cb7714610957578063d505accf1461096a57600080fd5b8063ad004e2011610183578063ad004e201461081a578063ad5c464814610822578063af1df25514610849578063b3d7f6b914610870578063b460af9414610883578063b8dc491b14610896578063ba087652146108a9578063bb27280b146108bc578063bdc8144b146108cf57600080fd5b806394bf804d146106a657806395d89b41146106b957806396d64879146106c15780639af1d35a146106e4578063a4da2d021461075f578063a59a997314610786578063a9059cbb146107ad578063abd3f612146107c0578063ac353510146107f357600080fd5b806337a4e834116102d15780636f2293ab116102695780636f2293ab1461061557806370a0823114610628578063715018a61461064857806372163715146106505780637ecebe001461065957806383b4918b146106795780638da5cb5b1461068c5780638e0bae7f14610694578063918f86741461069d57600080fd5b806337a4e8341461055f57806338d52e0f146105675780633982aabd1461057a5780633dc6eabf1461059a578063402d267d146105a257806348ccda3c146105b55780634cdad506146105dc5780636e553f65146105ef5780636e85f1831461060257600080fd5b806318160ddd1161034457806318160ddd146104775780631c17b946146104805780631fc29c011461048857806323b872dd146104bc5780632a5bf6d2146104cf57806330adf81f146104ef578063313ce5671461051657806334fbc9a11461054f5780633644e5151461055757600080fd5b806301e1d114146103a257806306fdde03146103bd57806307a2d13a146103d257806308f43333146103e5578063095ea7b3146104245780630a28a47714610447578063148349381461045a57806315f4c61114610462575b600080fd5b6103aa610a4c565b6040519081526020015b60405180910390f35b6103c5610a6d565b6040516103b49190613e44565b6103aa6103e0366004613e99565b610afb565b6103f86103f3366004613ec7565b610b4b565b604080516001600160701b03948516815293909216602084015263ffffffff16908201526060016103b4565b610437610432366004613ec7565b610b9a565b60405190151581526020016103b4565b6103aa610455366004613e99565b610c07565b6103aa610c33565b610475610470366004613faa565b610ca5565b005b6103aa60025481565b6103aa610f17565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6040516103b491906140b4565b6104376104ca3660046140c8565b610f48565b6104e26104dd366004614109565b610f5f565b6040516103b49190614126565b6103aa7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b61053d7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016103b4565b6103aa606481565b6103aa610ffb565b610475611051565b6007546104af906001600160a01b031681565b6103aa610588366004614109565b600a6020526000908152604090205481565b6104756112d1565b6103aa6105b0366004614109565b611390565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6103aa6105ea366004613e99565b61144f565b6103aa6105fd366004614191565b61145a565b610475610610366004613e99565b6114f3565b6104376106233660046141d6565b611568565b6103aa610636366004614109565b60036020526000908152604090205481565b610475611948565b6103aa60105481565b6103aa610667366004614109565b60056020526000908152604090205481565b610475610687366004613e99565b611983565b6104af611dcb565b6103aa600f5481565b6103aa61271081565b6103aa6106b4366004614191565b611dda565b6103c5611e75565b6104376106cf366004614109565b600b6020526000908152604090205460ff1681565b600d54600e5461071f916001600160701b0380821692600160701b808404831693600160e01b900463ffffffff169280831692919091041685565b604080516001600160701b039687168152948616602086015263ffffffff909316928401929092528316606083015291909116608082015260a0016103b4565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6104376107bb366004613ec7565b611e82565b6107d36107ce366004614109565b611e91565b6040805194855260208501939093529183015260608201526080016103b4565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6103aa612029565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6103aa61087e366004613e99565b6121f3565b6103aa610891366004614227565b612257565b6104756108a4366004614269565b6122a0565b6103aa6108b7366004614227565b612403565b6104756108ca366004614297565b612418565b6104756108dd366004613e99565b6124b9565b6012546104379060ff1681565b6008546104af906001600160a01b031681565b60085461053d90600160a01b900460ff1681565b610475612526565b6103aa61092c366004614109565b61270b565b6103aa61093f366004613e99565b612719565b6104756109523660046142ca565b612760565b6103aa610965366004614109565b6127ef565b6104756109783660046142f7565b612901565b6103aa61098b366004614109565b6001600160a01b031660009081526003602052604090205490565b6103aa6109b4366004614269565b600460209081526000928352604080842090915290825290205481565b6104756109df366004613e99565b612b40565b6104af7f000000000000000000000000000000000000000000000000000000000000000081565b6103aa6103e881565b6103aa60115481565b6103aa610a2b366004613e99565b612bad565b610475610a3e366004614109565b612bb8565b6103aa600c5481565b6000610a56610c33565b610a5e610f17565b610a68919061437e565b905090565b60008054610a7a90614396565b80601f0160208091040260200160405190810160405280929190818152602001828054610aa690614396565b8015610af35780601f10610ac857610100808354040283529160200191610af3565b820191906000526020600020905b815481529060010190602001808311610ad657829003601f168201915b505050505081565b600080610b0783612cc1565b600854909150610b449082907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b9392505050565b60096020528160005260406000208181548110610b6757600080fd5b6000918252602090912001546001600160701b038082169350600160701b8204169150600160e01b900463ffffffff1683565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610bf59086815260200190565b60405180910390a35060015b92915050565b6002546000908015610c2c57610c2781610c1f610a4c565b859190612cec565b610b44565b5090919050565b6007546040516370a0823160e01b81526000916001600160a01b0316906370a0823190610c649030906004016140b4565b602060405180830381865afa158015610c81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6891906143d1565b33610cae611dcb565b6001600160a01b031614610cdd5760405162461bcd60e51b8152600401610cd4906143ea565b60405180910390fd5b60125460ff1615610d0157604051632f22819760e11b815260040160405180910390fd5b6000805b8060081480610d3d5750600085610d1d83600161437e565b60098110610d2d57610d2d61441f565b60200201516001600160a01b0316145b15610d6057848160098110610d5457610d5461441f565b60200201519150610d72565b610d6b60028261437e565b9050610d05565b506007546001600160a01b0382811691161415610da4578060405163a337612b60e01b8152600401610cd491906140b4565b600754610dbc906001600160a01b0316600019612d1a565b6000610dc6610c33565b600754909150610e00906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000083612eba565b604051630d4f290960e21b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063353ca42490610e55908990899087908a90600401614435565b6020604051808303816000875af1158015610e74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9891906143d1565b6007549091506001600160a01b0316610eb084612f38565b600754610ec6906001600160a01b03168361319e565b42600c556040518281526001600160a01b0380861691908316907fb0850b8e0f9e8315dde3c9f9f31138283e6bbe16cd29e8552eb1dcdf9fac9e3b906020015b60405180910390a350505050505050565b6008546040516370a0823160e01b81526000916001600160a01b0316906370a0823190610c649030906004016140b4565b6000610f578484846000611568565b949350505050565b6001600160a01b0381166000908152600960209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610ff057600084815260209081902060408051606081018252918501546001600160701b038082168452600160701b82041683850152600160e01b900463ffffffff1690820152825260019092019101610f97565b505050509050919050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461102c57610a6861338b565b507f000000000000000000000000000000000000000000000000000000000000000090565b600061105b613425565b600d54909150600160701b90046001600160701b0316808211156110c35761108381836144d1565b600d805460009061109e9084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b600d546000906110e090600160e01b900463ffffffff16426144d1565b905060006301e133806127106064846110f7613425565b6111019190614513565b61110b9190614513565b6111159190614548565b61111f9190614548565b9050600061112c826134dd565b600d80546001600160e01b0316600160e01b4263ffffffff160217905590506111553082613515565b600d546001600160701b03166000611172826103e861271061356f565b9050600061117f826134dd565b600d80546001600160701b0319169055905061119b3082613515565b600e80548591906000906111b99084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b0316021790555080600d600101600e8282829054906101000a90046001600160701b031661120491906144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055507fbb0dab1b2fba411c08a912a26b307680de2fcebcfa94469607a1a2ca6a5d8bf48460405161125991815260200190565b60405180910390a16040518181527f87ddcf7d00014b95f4de1ab15232c30192eabc2849f9cb6c677a5a79bf74e9ab9060200160405180910390a15050505050506112a2613425565b600d80546001600160701b0392909216600160701b02600160701b600160e01b03199092169190911790555050565b336112da611dcb565b6001600160a01b0316146113005760405162461bcd60e51b8152600401610cd4906143ea565b60125460ff161561132457604051632f22819760e11b815260040160405180910390fd5b600061132e610c33565b600754909150611347906001600160a01b03168261319e565b42600c556007546040518281526001600160a01b03909116907fb6f4b9255ee989b1844a8e6b7da8906b81200c38f7b3f4f1ac31e9a241c757509060200160405180910390a250565b60125460009060ff16156113a657506000919050565b6000196011541480156113bc5750600019601054145b15611414576008546113f890600160a01b900460ff167f000000000000000000000000000000000000000000000000000000000000000061455c565b61140390600a614663565b610c01906001600160701b03614548565b600061142b611422846127ef565b6011549061358e565b9050600061144361143a610a4c565b6010549061358e565b9050610f5782826135a8565b6000610c0182610afb565b6007546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061148f9033906004016140b4565b602060405180830381865afa1580156114ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d091906143d1565b9050808411156114de578093505b6114ea846000856135b7565b95945050505050565b336114fc611dcb565b6001600160a01b0316146115225760405162461bcd60e51b8152600401610cd4906143ea565b600f80549082905560408051828152602081018490527f513ac19cbbaaad4e450c732ed37635178b7d83bf8e84a940ffe7e052c9c7caa291015b60405180910390a15050565b60006001600160a01b03851633146115d8576001600160a01b038516600090815260046020908152604080832033845290915290205460001981146115d6576115b184826144d1565b6001600160a01b03871660009081526004602090815260408083203384529091529020555b505b6001600160a01b038516600090815260036020526040812080548592906116009084906144d1565b90915550506001600160a01b038085166000908152600360209081526040808320805488019055928816825260098152828220600a9091529190205484905b82548110156118d657600083828154811061165c5761165c61441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff1610868015611689575080155b156116955750506118c4565b8154600160701b90046001600160701b031660006116b386836135a8565b84549091506000906116cf906001600160701b03168385612cec565b905083156116ec578454600160701b600160e01b0316855561172e565b8454819086906000906117099084906001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b845482908690600e90611752908490600160701b90046001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b03160217905550600960008d6001600160a01b03166001600160a01b031681526020019081526020016000206040518060600160405280866117b157836117b4565b60005b6001600160701b03168152602001846001600160701b03168152602001866117ea578754600160e01b900463ffffffff166117ed565b60005b63ffffffff9081169091528254600181018455600093845260209384902083519101805494840151604090940151909216600160e01b026001600160e01b036001600160701b03948516600160701b026001600160e01b03199096169490921693909317939093179290921617905561186682886144d1565b9650866118be57896118b4578454600160701b90046001600160701b03166118985761189386600161437e565b61189a565b855b6001600160a01b038e166000908152600a60205260409020555b50505050506118d6565b50505050505b806118ce8161469a565b91505061163f565b5080156119005760405163a09b101160e01b81526004810182905260248101869052604401610cd4565b856001600160a01b0316876001600160a01b03166000805160206149ea8339815191528760405161193391815260200190565b60405180910390a35060019695505050505050565b33611951611dcb565b6001600160a01b0316146119775760405162461bcd60e51b8152600401610cd4906143ea565b61198160006137b7565b565b3361198c611dcb565b6001600160a01b0316146119b25760405162461bcd60e51b8152600401610cd4906143ea565b6040516301e9a69560e41b815230600482015260001960248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690631e9a695090604401600060405180830381600087803b158015611a1b57600080fd5b505af1158015611a2f573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691506370a0823190611a829030906004016140b4565b602060405180830381865afa158015611a9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac391906143d1565b6040805160038082526080820190925291925060009190602082016060803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110611b1e57611b1e61441f565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110611b7257611b7261441f565b6001600160a01b039283166020918202929092010152600754825191169082906002908110611ba357611ba361441f565b6001600160a01b039283166020918202929092010152611c06907f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000084612eba565b60006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166338ed173984868530611c4642603c61437e565b6040518663ffffffff1660e01b8152600401611c669594939291906146f9565b6000604051808303816000875af1158015611c85573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611cad9190810190614735565b905060008160018351611cc091906144d1565b81518110611cd057611cd061441f565b60200260200101519050611d1e600860149054906101000a900460ff167f000000000000000000000000000000000000000000000000000000000000000083612c589092919063ffffffff16565b600d8054600090611d399084906001600160701b03166144e8565b82546001600160701b039182166101009390930a92830291909202199091161790555060125460ff16611d7c57600754611d7c906001600160a01b03168261319e565b60075460408051868152602081018490526001600160a01b03909216917fc003f45bc224d116b6d079100d4ab57a5b9633244c47a5a92a176c5b79a85f28910160405180910390a25050505050565b6006546001600160a01b031690565b6007546040516370a0823160e01b81526000918291611e52916001600160a01b0316906370a0823190611e119033906004016140b4565b602060405180830381865afa158015611e2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2b91906143d1565b905080841115611e60578093505b611e6c600085856135b7565b50949350505050565b60018054610a7a90614396565b6000610b443384846000611568565b6001600160a01b038116600090815260096020526040812081908190819081611ec1670de0b6b3a7640000612cc1565b6001600160a01b0388166000908152600a60205260409020549091505b8254811015611fa6576000838281548110611efb57611efb61441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff161015611f5c578054600160701b90046001600160701b0316611f3e818a61437e565b9850611f4a8185613809565b611f54908861437e565b965050611f93565b8054611f7890600160701b90046001600160701b03168861437e565b8154909750611f90906001600160701b03168661437e565b94505b5080611f9e8161469a565b915050611ede565b50600854611fe19085907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b60085490945061201e9084907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b925050509193509193565b600033612034611dcb565b6001600160a01b03161461205a5760405162461bcd60e51b8152600401610cd4906143ea565b60408051600180825281830190925260009160208083019080368337505060085482519293506001600160a01b03169183915060009061209c5761209c61441f565b6001600160a01b039283166020918202929092010152604051633111e7b360e01b81527f000000000000000000000000000000000000000000000000000000000000000090911690633111e7b3906120fe9084906000199030906004016147db565b6020604051808303816000875af115801561211d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214191906143d1565b91507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663787a08a66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561219e57600080fd5b505af11580156121b2573d6000803e3d6000fd5b505050507f8ca0188d9770b383d1a7a2ddfe5e0c1f029084481a53697d6c51525c47a8d88e826040516121e791815260200190565b60405180910390a15090565b6002546000908181156122185761221361220b61381e565b859084612cec565b61221a565b835b600854909150610f579082907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b600854600090612293908590600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000000612c58565b93506114ea848484613830565b336122a9611dcb565b6001600160a01b0316146122cf5760405162461bcd60e51b8152600401610cd4906143ea565b6007546001600160a01b03838116911614806122f857506008546001600160a01b038381169116145b8061230b57506001600160a01b03821630145b1561232b57816040516339b8549160e01b8152600401610cd491906140b4565b6040516370a0823160e01b81526000906001600160a01b038416906370a082319061235a9030906004016140b4565b602060405180830381865afa158015612377573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239b91906143d1565b90506123b16001600160a01b0384168383613bf4565b816001600160a01b0316836001600160a01b03167fed679328aebf74ede77ae09efcf36e90244f83643dadac1c2d9f0b21a46f6ab7836040516123f691815260200190565b60405180910390a3505050565b6000611e6c61241185612cc1565b8484613830565b33612421611dcb565b6001600160a01b0316146124475760405162461bcd60e51b8152600401610cd4906143ea565b6012805460ff1916831580159190911790915582906124635750805b1561248057600754612480906001600160a01b0316600019612d1a565b60408051831515815282151560208201527f7ac7d23f223201cd219bf262dee0820ebf6aa5ba682fbd5dd9f849bbefd05358910161155c565b336124c2611dcb565b6001600160a01b0316146124e85760405162461bcd60e51b8152600401610cd4906143ea565b601180549082905560408051828152602081018490527fcfb5a454b8aa7dc04ecb5bc1410b2a57969ca1d67f66d565196f60c6f9975404910161155c565b3361252f611dcb565b6001600160a01b0316146125555760405162461bcd60e51b8152600401610cd4906143ea565b6040516370a0823160e01b815260009030906370a082319061257b9083906004016140b4565b602060405180830381865afa158015612598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125bc91906143d1565b905060006125c98261144f565b90506125d53083613c6d565b6125de81613ccf565b600754612615906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000083612eba565b600754600f54604051631ffbe7f960e01b81526001600160a01b0392831660048201526024810191909152604481018390527f000000000000000000000000000000000000000000000000000000000000000090911690631ffbe7f990606401600060405180830381600087803b15801561268f57600080fd5b505af11580156126a3573d6000803e3d6000fd5b5050600e54604080516001600160701b038084168252600160701b90930490921660208301527f393c7eed3c13cd314ecf9287699a4c4737ef3422abf88199b0009cbdbc83e5c8935001905060405180910390a15050600e80546001600160e01b0319169055565b6000610c0161093f83611390565b600854600090612755908390600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000000612c58565b9150610c01826134dd565b33612769611dcb565b6001600160a01b03161461278f5760405162461bcd60e51b8152600401610cd4906143ea565b6001600160a01b0382166000908152600b60205260409020805460ff1916821580159182179092556127ce57506007546001600160a01b038381169116145b156127eb576007546127eb906001600160a01b0316600019612d1a565b5050565b6001600160a01b0381166000908152600960205260408120818061281a670de0b6b3a7640000612cc1565b6001600160a01b0386166000908152600a60205260409020549091505b83548110156128c65760008482815481106128545761285461441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff161061288a5780546001600160701b03166128a6565b80546128a690600160701b90046001600160701b031684613809565b6128b0908561437e565b93505080806128be9061469a565b915050612837565b506008546114ea9083907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b4284101561294b5760405162461bcd60e51b815260206004820152601760248201527614115493525517d11150511312539157d1561412549151604a1b6044820152606401610cd4565b6000612955610ffb565b6001600160a01b0389811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612a6e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590612aa45750886001600160a01b0316816001600160a01b0316145b612ae15760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610cd4565b6001600160a01b0390811660009081526004602090815260408083208b8516808552908352928190208a905551898152919350918a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101610f06565b33612b49611dcb565b6001600160a01b031614612b6f5760405162461bcd60e51b8152600401610cd4906143ea565b601080549082905560408051828152602081018490527f1f21432dd7b8ead64d2e7c06a74baf13783b2d2f7153f099e2c4cabc3c5dbec6910161155c565b6000610c0182612719565b33612bc1611dcb565b6001600160a01b031614612be75760405162461bcd60e51b8152600401610cd4906143ea565b6001600160a01b038116612c4c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610cd4565b612c55816137b7565b50565b60008160ff168360ff161415612c6f575082610b44565b8160ff168360ff161015612ca357612c87838361455c565b612c9290600a614663565b612c9c9085614513565b9050610b44565b612c9c84612cb1848661455c565b612cbc90600a614663565b613d02565b6000600254600014612ce857612ce3612cd861381e565b60025484919061356f565b610c01565b5090565b828202811515841585830485141716612d0457600080fd5b6001826001830304018115150290509392505050565b6000612d24613425565b600d54909150600160701b90046001600160701b031680821115612d8c57612d4c81836144d1565b600d8054600090612d679084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b6000612d96610f17565b1115612e8157604051631a4ca37b60e21b81526001600160a01b038581166004830152602482018590523060448301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906369328dec906064016020604051808303816000875af1158015612e16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e3a91906143d1565b9050846001600160a01b03167f6407790cdabc5d219eaf901091d6beccc475533065c2fbd374c8a32b1c66795882604051612e7791815260200190565b60405180910390a2505b612e89613425565b600d80546001600160701b0392909216600160701b02600160701b600160e01b031990921691909117905550505050565b600060405163095ea7b360e01b81526001600160a01b03841660048201528260248201526000806044836000895af1915050612ef581613d34565b612f325760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610cd4565b50505050565b6040516335ea6a7560e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906335ea6a7590612f879085906004016140b4565b61018060405180830381865afa158015612fa5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc9919061483b565b5050505097505050505050505060006001600160a01b0316816001600160a01b0316141561300c5781604051630a5c5e7d60e11b8152600401610cd491906140b4565b6000600860149054906101000a900460ff1690506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613060573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613084919061491c565b90507f000000000000000000000000000000000000000000000000000000000000000060ff168160ff1611156130fa57604051630651982f60e11b815260ff80831660048301527f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610cd4565b60ff82161580159061311257508060ff168260ff1614155b15613151576000196011541461313457601154613130908383612c58565b6011555b600019601054146131515760105461314d908383612c58565b6010555b600780546001600160a01b039586166001600160a01b031991821617909155600880549490951660ff909216600160a01b02166001600160a81b0319909316929092179190911790915550565b60006131a8613425565b600d54909150600160701b90046001600160701b031680821115613210576131d081836144d1565b600d80546000906131eb9084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b6001600160a01b0384166000908152600b602052604090205460ff1661324b57836040516386433f2b60e01b8152600401610cd491906140b4565b600d54600160e01b900463ffffffff1661327d57600d80546001600160e01b0316600160e01b4263ffffffff16021790555b6132b16001600160a01b0385167f000000000000000000000000000000000000000000000000000000000000000085612eba565b60405163e8eda9df60e01b81526001600160a01b03858116600483015260248201859052306044830152600060648301527f0000000000000000000000000000000000000000000000000000000000000000169063e8eda9df90608401600060405180830381600087803b15801561332857600080fd5b505af115801561333c573d6000803e3d6000fd5b50505050836001600160a01b03167f3b1270fa6f77c9af94834571cf5274944e8712de6cebea9ec3d8b3452c0533088460405161337b91815260200190565b60405180910390a2612e89613425565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516133bd9190614939565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6008546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061345a9030906004016140b4565b602060405180830381865afa158015613477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349b91906143d1565b6008549091506134d7908290600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000000612c58565b91505090565b6000806134e861381e565b6002549091508115806134f9575080155b61350d5761350884828461356f565b610f57565b509192915050565b8060026000828254613527919061437e565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481526000805160206149ea83398151915291015b60405180910390a35050565b82820281151584158583048514171661358757600080fd5b0492915050565b600081831161359e576000610b44565b610b4482846144d1565b6000818310610c2c5781610b44565b601254600090819060ff16156135e057604051632f22819760e11b815260040160405180910390fd5b600084116135f9576135f185612bad565b935083613606565b613602846121f3565b9450845b50836136255760405163df46449f60e01b815260040160405180910390fd5b61362e83611390565b851115613661578461363f84611390565b6040516323dc290560e21b815260048101929092526024820152604401610cd4565b600754613679906001600160a01b0316333088613d7b565b6136838385613515565b6001600160a01b038316600090815260096020526040908190208151606081019092526008549091829181906136e5908a90600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000000612c58565b6001600160701b03908116825288811660208084019190915263ffffffff42811660409485015285546001810187556000968752958290208551960180548684015196860151909216600160e01b026001600160e01b03968516600160701b026001600160e01b0319909316979094169690961717939093161790925560075482518981529182018890526001600160a01b03908116929087169133917f5fe47ed6d4225326d3303476197d782ded5a4e9c14f479dc9ec4992af4e85d59910160405180910390a45093949293505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000610b448383670de0b6b3a764000061356f565b6000613828613e0f565b610a5e613425565b6001600160a01b03811660009081526003602052604081205481906138685760405163df46449f60e01b815260040160405180910390fd5b846138865760405163332215e760e11b815260040160405180910390fd5b6001600160a01b038316600090815260096020526040812086826138b1670de0b6b3a7640000612cc1565b6001600160a01b0388166000908152600a60205260409020549091505b8354811015613ab35760008482815481106138eb576138eb61441f565b600091825260208220600c5491018054909350600160e01b900463ffffffff161090816139225782546001600160701b031661393e565b825461393e90600160701b90046001600160701b031686613809565b9050600061394c87836135a8565b845490915060009061396f90600160701b90046001600160701b03168385612cec565b9050831561398c578454600160701b600160e01b031685556139ce565b8454829086906000906139a99084906001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b845481908690600e906139f2908490600160701b90046001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b03160217905550808a613a22919061437e565b9950613a2e82896144d1565b8954909850613a3f906001906144d1565b861480613a4a575087155b15613a9b578454600160701b90046001600160701b0316613a7557613a7086600161437e565b613a77565b855b6001600160a01b038e166000908152600a602052604090205550613ab39350505050565b50505050508080613aab9061469a565b9150506138ce565b50336001600160a01b03881614613b22576001600160a01b03871660009081526004602090815260408083203384529091529020546000198114613b2057613afb85826144d1565b6001600160a01b03891660009081526004602090815260408083203384529091529020555b505b613b2c8785613c6d565b613b36828a6144d1565b600854909950613b73908a907f000000000000000000000000000000000000000000000000000000000000000090600160a01b900460ff16612c58565b9850613b7e89613ccf565b600754613b95906001600160a01b0316898b613bf4565b600754604080518b8152602081018790526001600160a01b03928316928a811692908c16917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a450969791965090945050505050565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af1915050613c2f81613d34565b612f325760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610cd4565b6001600160a01b03821660009081526003602052604081208054839290613c959084906144d1565b90915550506002805482900390556040518181526000906001600160a01b038416906000805160206149ea83398151915290602001613563565b6000613cd9610c33565b9050808211156127eb576007546127eb906001600160a01b0316613cfd83856144d1565b612d1a565b6000613d0e82846149d5565b15613d1a576001613d1d565b60005b60ff16613d2a8385614548565b610b44919061437e565b60003d82613d4657806000803e806000fd5b8060208114613d5e578015613d6f5760009250613d74565b816000803e60005115159250613d74565b600192505b5050919050565b60006040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260008060648360008a5af1915050613dc581613d34565b613e085760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610cd4565b5050505050565b6007546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061345a9030906004016140b4565b600060208083528351808285015260005b81811015613e7157858101830151858201604001528201613e55565b81811115613e83576000604083870101525b50601f01601f1916929092016040019392505050565b600060208284031215613eab57600080fd5b5035919050565b6001600160a01b0381168114612c5557600080fd5b60008060408385031215613eda57600080fd5b8235613ee581613eb2565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b60405290565b6040516080810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b6040516060810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b604051601f8201601f1916810167ffffffffffffffff81118282101715613fa257613fa2613ef3565b604052919050565b60008060006102c08486031215613fc057600080fd5b601f8581860112613fd057600080fd5b613fd8613f09565b80610120870188811115613feb57600080fd5b875b8181101561400e57803561400081613eb2565b845260209384019301613fed565b508196508861013f89011261402257600080fd5b61402a613f33565b92508291506102a088018981111561404157600080fd5b808210156140a45789858301126140585760008081fd5b614060613f56565b80606084018c8111156140735760008081fd5b845b8181101561408d578035845260209384019301614075565b505085525060209093019260609190910190614041565b9699919850509435955050505050565b6001600160a01b0391909116815260200190565b6000806000606084860312156140dd57600080fd5b83356140e881613eb2565b925060208401356140f881613eb2565b929592945050506040919091013590565b60006020828403121561411b57600080fd5b8135610b4481613eb2565b602080825282518282018190526000919060409081850190868401855b8281101561418457815180516001600160701b03908116865287820151168786015285015163ffffffff168585015260609093019290850190600101614143565b5091979650505050505050565b600080604083850312156141a457600080fd5b8235915060208301356141b681613eb2565b809150509250929050565b803580151581146141d157600080fd5b919050565b600080600080608085870312156141ec57600080fd5b84356141f781613eb2565b9350602085013561420781613eb2565b92506040850135915061421c606086016141c1565b905092959194509250565b60008060006060848603121561423c57600080fd5b83359250602084013561424e81613eb2565b9150604084013561425e81613eb2565b809150509250925092565b6000806040838503121561427c57600080fd5b823561428781613eb2565b915060208301356141b681613eb2565b600080604083850312156142aa57600080fd5b6142b3836141c1565b91506142c1602084016141c1565b90509250929050565b600080604083850312156142dd57600080fd5b82356142b381613eb2565b60ff81168114612c5557600080fd5b600080600080600080600060e0888a03121561431257600080fd5b873561431d81613eb2565b9650602088013561432d81613eb2565b95506040880135945060608801359350608088013561434b816142e8565b9699959850939692959460a0840135945060c09093013592915050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561439157614391614368565b500190565b600181811c908216806143aa57607f821691505b602082108114156143cb57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156143e357600080fd5b5051919050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6102e08101818660005b60098110156144675781516001600160a01b031683526020928301929091019060010161443f565b50505061012082018560005b60048110156144ba5781518360005b60038110156144a1578251825260209283019290910190600101614482565b5050506060929092019160209190910190600101614473565b5050506102a08201939093526102c0015292915050565b6000828210156144e3576144e3614368565b500390565b60006001600160701b0382811684821680830382111561450a5761450a614368565b01949350505050565b600081600019048311821515161561452d5761452d614368565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261455757614557614532565b500490565b600060ff821660ff84168082101561457657614576614368565b90039392505050565b600181815b808511156145ba5781600019048211156145a0576145a0614368565b808516156145ad57918102915b93841c9390800290614584565b509250929050565b6000826145d157506001610c01565b816145de57506000610c01565b81600181146145f457600281146145fe5761461a565b6001915050610c01565b60ff84111561460f5761460f614368565b50506001821b610c01565b5060208310610133831016604e8410600b841016171561463d575081810a610c01565b614647838361457f565b806000190482111561465b5761465b614368565b029392505050565b6000610b4460ff8416836145c2565b60006001600160701b038381169083168181101561469257614692614368565b039392505050565b60006000198214156146ae576146ae614368565b5060010190565b600081518084526020808501945080840160005b838110156146ee5781516001600160a01b0316875295820195908201906001016146c9565b509495945050505050565b85815284602082015260a06040820152600061471860a08301866146b5565b6001600160a01b0394909416606083015250608001529392505050565b6000602080838503121561474857600080fd5b825167ffffffffffffffff8082111561476057600080fd5b818501915085601f83011261477457600080fd5b81518181111561478657614786613ef3565b8060051b9150614797848301613f79565b81815291830184019184810190888411156147b157600080fd5b938501935b838510156147cf578451825293850193908501906147b6565b98975050505050505050565b6060815260006147ee60608301866146b5565b6020830194909452506001600160a01b0391909116604090910152919050565b80516001600160801b03811681146141d157600080fd5b80516141d181613eb2565b80516141d1816142e8565b6000806000806000806000806000806000806101808d8f03121561485e57600080fd5b8c519b5061486e60208e0161480e565b9a5061487c60408e0161480e565b995061488a60608e0161480e565b985061489860808e0161480e565b97506148a660a08e0161480e565b965060c08d015164ffffffffff811681146148c057600080fd5b95506148ce60e08e01614825565b94506148dd6101008e01614825565b93506148ec6101208e01614825565b92506148fb6101408e01614825565b915061490a6101608e01614830565b90509295989b509295989b509295989b565b60006020828403121561492e57600080fd5b8151610b44816142e8565b600080835481600182811c91508083168061495557607f831692505b602080841082141561497557634e487b7160e01b86526022600452602486fd5b818015614989576001811461499a576149c7565b60ff198616895284890196506149c7565b60008a81526020902060005b868110156149bf5781548b8201529085019083016149a6565b505084890196505b509498975050505050505050565b6000826149e4576149e4614532565b50069056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa164736f6c634300080b000a536f6d6d656c696572204161766520563220537461626c65636f696e2043656c6c6172204c5020546f6b656e000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000014000000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd7000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a9000000000000000000000000d784927ff2f95ba542bfc824c8a8a98f3495f6b500000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f70000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f50000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae9000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000008000000000000000000000000056fd409e1d7a124bd7017459dfea2f387b6d5cd0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000956f47f50a910163d8bf957cf5846d573e7f87ca000000000000000000000000853d955acef822db058eb8505911ed77f175b99e00000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f510000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061039d5760003560e01c806394bf804d116101eb578063bf86d69011610110578063d905777e116100a8578063d905777e1461097d578063dd62ed3e146109a6578063df05a52a146109d1578063e9240c2d146109e4578063e93f566514610a0b578063ecf7085814610a14578063ef8b30f714610a1d578063f2fde38b14610a30578063f82d4d9b14610a4357600080fd5b8063bf86d690146108e2578063c17f6740146108ef578063c2d4160114610902578063c2fbe7bc14610916578063c63d75b61461091e578063c6e6f59214610931578063cab5923814610944578063ce96cb7714610957578063d505accf1461096a57600080fd5b8063ad004e2011610183578063ad004e201461081a578063ad5c464814610822578063af1df25514610849578063b3d7f6b914610870578063b460af9414610883578063b8dc491b14610896578063ba087652146108a9578063bb27280b146108bc578063bdc8144b146108cf57600080fd5b806394bf804d146106a657806395d89b41146106b957806396d64879146106c15780639af1d35a146106e4578063a4da2d021461075f578063a59a997314610786578063a9059cbb146107ad578063abd3f612146107c0578063ac353510146107f357600080fd5b806337a4e834116102d15780636f2293ab116102695780636f2293ab1461061557806370a0823114610628578063715018a61461064857806372163715146106505780637ecebe001461065957806383b4918b146106795780638da5cb5b1461068c5780638e0bae7f14610694578063918f86741461069d57600080fd5b806337a4e8341461055f57806338d52e0f146105675780633982aabd1461057a5780633dc6eabf1461059a578063402d267d146105a257806348ccda3c146105b55780634cdad506146105dc5780636e553f65146105ef5780636e85f1831461060257600080fd5b806318160ddd1161034457806318160ddd146104775780631c17b946146104805780631fc29c011461048857806323b872dd146104bc5780632a5bf6d2146104cf57806330adf81f146104ef578063313ce5671461051657806334fbc9a11461054f5780633644e5151461055757600080fd5b806301e1d114146103a257806306fdde03146103bd57806307a2d13a146103d257806308f43333146103e5578063095ea7b3146104245780630a28a47714610447578063148349381461045a57806315f4c61114610462575b600080fd5b6103aa610a4c565b6040519081526020015b60405180910390f35b6103c5610a6d565b6040516103b49190613e44565b6103aa6103e0366004613e99565b610afb565b6103f86103f3366004613ec7565b610b4b565b604080516001600160701b03948516815293909216602084015263ffffffff16908201526060016103b4565b610437610432366004613ec7565b610b9a565b60405190151581526020016103b4565b6103aa610455366004613e99565b610c07565b6103aa610c33565b610475610470366004613faa565b610ca5565b005b6103aa60025481565b6103aa610f17565b6104af7f0000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f581565b6040516103b491906140b4565b6104376104ca3660046140c8565b610f48565b6104e26104dd366004614109565b610f5f565b6040516103b49190614126565b6103aa7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b61053d7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff90911681526020016103b4565b6103aa606481565b6103aa610ffb565b610475611051565b6007546104af906001600160a01b031681565b6103aa610588366004614109565b600a6020526000908152604090205481565b6104756112d1565b6103aa6105b0366004614109565b611390565b6104af7f0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae981565b6103aa6105ea366004613e99565b61144f565b6103aa6105fd366004614191565b61145a565b610475610610366004613e99565b6114f3565b6104376106233660046141d6565b611568565b6103aa610636366004614109565b60036020526000908152604090205481565b610475611948565b6103aa60105481565b6103aa610667366004614109565b60056020526000908152604090205481565b610475610687366004613e99565b611983565b6104af611dcb565b6103aa600f5481565b6103aa61271081565b6103aa6106b4366004614191565b611dda565b6103c5611e75565b6104376106cf366004614109565b600b6020526000908152604090205460ff1681565b600d54600e5461071f916001600160701b0380821692600160701b808404831693600160e01b900463ffffffff169280831692919091041685565b604080516001600160701b039687168152948616602086015263ffffffff909316928401929092528316606083015291909116608082015260a0016103b4565b6104af7f00000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f781565b6104af7f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a981565b6104376107bb366004613ec7565b611e82565b6107d36107ce366004614109565b611e91565b6040805194855260208501939093529183015260608201526080016103b4565b6104af7f00000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd781565b6103aa612029565b6104af7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6104af7f000000000000000000000000d784927ff2f95ba542bfc824c8a8a98f3495f6b581565b6103aa61087e366004613e99565b6121f3565b6103aa610891366004614227565b612257565b6104756108a4366004614269565b6122a0565b6103aa6108b7366004614227565b612403565b6104756108ca366004614297565b612418565b6104756108dd366004613e99565b6124b9565b6012546104379060ff1681565b6008546104af906001600160a01b031681565b60085461053d90600160a01b900460ff1681565b610475612526565b6103aa61092c366004614109565b61270b565b6103aa61093f366004613e99565b612719565b6104756109523660046142ca565b612760565b6103aa610965366004614109565b6127ef565b6104756109783660046142f7565b612901565b6103aa61098b366004614109565b6001600160a01b031660009081526003602052604090205490565b6103aa6109b4366004614269565b600460209081526000928352604080842090915290825290205481565b6104756109df366004613e99565b612b40565b6104af7f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f81565b6103aa6103e881565b6103aa60115481565b6103aa610a2b366004613e99565b612bad565b610475610a3e366004614109565b612bb8565b6103aa600c5481565b6000610a56610c33565b610a5e610f17565b610a68919061437e565b905090565b60008054610a7a90614396565b80601f0160208091040260200160405190810160405280929190818152602001828054610aa690614396565b8015610af35780601f10610ac857610100808354040283529160200191610af3565b820191906000526020600020905b815481529060010190602001808311610ad657829003601f168201915b505050505081565b600080610b0783612cc1565b600854909150610b449082907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b9392505050565b60096020528160005260406000208181548110610b6757600080fd5b6000918252602090912001546001600160701b038082169350600160701b8204169150600160e01b900463ffffffff1683565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610bf59086815260200190565b60405180910390a35060015b92915050565b6002546000908015610c2c57610c2781610c1f610a4c565b859190612cec565b610b44565b5090919050565b6007546040516370a0823160e01b81526000916001600160a01b0316906370a0823190610c649030906004016140b4565b602060405180830381865afa158015610c81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6891906143d1565b33610cae611dcb565b6001600160a01b031614610cdd5760405162461bcd60e51b8152600401610cd4906143ea565b60405180910390fd5b60125460ff1615610d0157604051632f22819760e11b815260040160405180910390fd5b6000805b8060081480610d3d5750600085610d1d83600161437e565b60098110610d2d57610d2d61441f565b60200201516001600160a01b0316145b15610d6057848160098110610d5457610d5461441f565b60200201519150610d72565b610d6b60028261437e565b9050610d05565b506007546001600160a01b0382811691161415610da4578060405163a337612b60e01b8152600401610cd491906140b4565b600754610dbc906001600160a01b0316600019612d1a565b6000610dc6610c33565b600754909150610e00906001600160a01b03167f00000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd783612eba565b604051630d4f290960e21b81526000906001600160a01b037f00000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd7169063353ca42490610e55908990899087908a90600401614435565b6020604051808303816000875af1158015610e74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9891906143d1565b6007549091506001600160a01b0316610eb084612f38565b600754610ec6906001600160a01b03168361319e565b42600c556040518281526001600160a01b0380861691908316907fb0850b8e0f9e8315dde3c9f9f31138283e6bbe16cd29e8552eb1dcdf9fac9e3b906020015b60405180910390a350505050505050565b6008546040516370a0823160e01b81526000916001600160a01b0316906370a0823190610c649030906004016140b4565b6000610f578484846000611568565b949350505050565b6001600160a01b0381166000908152600960209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610ff057600084815260209081902060408051606081018252918501546001600160701b038082168452600160701b82041683850152600160e01b900463ffffffff1690820152825260019092019101610f97565b505050509050919050565b60007f0000000000000000000000000000000000000000000000000000000000000001461461102c57610a6861338b565b507f85258705dc654a0db646767e791009ed916db427d8c70de4546705a69307b9e690565b600061105b613425565b600d54909150600160701b90046001600160701b0316808211156110c35761108381836144d1565b600d805460009061109e9084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b600d546000906110e090600160e01b900463ffffffff16426144d1565b905060006301e133806127106064846110f7613425565b6111019190614513565b61110b9190614513565b6111159190614548565b61111f9190614548565b9050600061112c826134dd565b600d80546001600160e01b0316600160e01b4263ffffffff160217905590506111553082613515565b600d546001600160701b03166000611172826103e861271061356f565b9050600061117f826134dd565b600d80546001600160701b0319169055905061119b3082613515565b600e80548591906000906111b99084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b0316021790555080600d600101600e8282829054906101000a90046001600160701b031661120491906144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055507fbb0dab1b2fba411c08a912a26b307680de2fcebcfa94469607a1a2ca6a5d8bf48460405161125991815260200190565b60405180910390a16040518181527f87ddcf7d00014b95f4de1ab15232c30192eabc2849f9cb6c677a5a79bf74e9ab9060200160405180910390a15050505050506112a2613425565b600d80546001600160701b0392909216600160701b02600160701b600160e01b03199092169190911790555050565b336112da611dcb565b6001600160a01b0316146113005760405162461bcd60e51b8152600401610cd4906143ea565b60125460ff161561132457604051632f22819760e11b815260040160405180910390fd5b600061132e610c33565b600754909150611347906001600160a01b03168261319e565b42600c556007546040518281526001600160a01b03909116907fb6f4b9255ee989b1844a8e6b7da8906b81200c38f7b3f4f1ac31e9a241c757509060200160405180910390a250565b60125460009060ff16156113a657506000919050565b6000196011541480156113bc5750600019601054145b15611414576008546113f890600160a01b900460ff167f000000000000000000000000000000000000000000000000000000000000001261455c565b61140390600a614663565b610c01906001600160701b03614548565b600061142b611422846127ef565b6011549061358e565b9050600061144361143a610a4c565b6010549061358e565b9050610f5782826135a8565b6000610c0182610afb565b6007546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061148f9033906004016140b4565b602060405180830381865afa1580156114ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d091906143d1565b9050808411156114de578093505b6114ea846000856135b7565b95945050505050565b336114fc611dcb565b6001600160a01b0316146115225760405162461bcd60e51b8152600401610cd4906143ea565b600f80549082905560408051828152602081018490527f513ac19cbbaaad4e450c732ed37635178b7d83bf8e84a940ffe7e052c9c7caa291015b60405180910390a15050565b60006001600160a01b03851633146115d8576001600160a01b038516600090815260046020908152604080832033845290915290205460001981146115d6576115b184826144d1565b6001600160a01b03871660009081526004602090815260408083203384529091529020555b505b6001600160a01b038516600090815260036020526040812080548592906116009084906144d1565b90915550506001600160a01b038085166000908152600360209081526040808320805488019055928816825260098152828220600a9091529190205484905b82548110156118d657600083828154811061165c5761165c61441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff1610868015611689575080155b156116955750506118c4565b8154600160701b90046001600160701b031660006116b386836135a8565b84549091506000906116cf906001600160701b03168385612cec565b905083156116ec578454600160701b600160e01b0316855561172e565b8454819086906000906117099084906001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b845482908690600e90611752908490600160701b90046001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b03160217905550600960008d6001600160a01b03166001600160a01b031681526020019081526020016000206040518060600160405280866117b157836117b4565b60005b6001600160701b03168152602001846001600160701b03168152602001866117ea578754600160e01b900463ffffffff166117ed565b60005b63ffffffff9081169091528254600181018455600093845260209384902083519101805494840151604090940151909216600160e01b026001600160e01b036001600160701b03948516600160701b026001600160e01b03199096169490921693909317939093179290921617905561186682886144d1565b9650866118be57896118b4578454600160701b90046001600160701b03166118985761189386600161437e565b61189a565b855b6001600160a01b038e166000908152600a60205260409020555b50505050506118d6565b50505050505b806118ce8161469a565b91505061163f565b5080156119005760405163a09b101160e01b81526004810182905260248101869052604401610cd4565b856001600160a01b0316876001600160a01b03166000805160206149ea8339815191528760405161193391815260200190565b60405180910390a35060019695505050505050565b33611951611dcb565b6001600160a01b0316146119775760405162461bcd60e51b8152600401610cd4906143ea565b61198160006137b7565b565b3361198c611dcb565b6001600160a01b0316146119b25760405162461bcd60e51b8152600401610cd4906143ea565b6040516301e9a69560e41b815230600482015260001960248201527f0000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f56001600160a01b031690631e9a695090604401600060405180830381600087803b158015611a1b57600080fd5b505af1158015611a2f573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b037f0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae91691506370a0823190611a829030906004016140b4565b602060405180830381865afa158015611a9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac391906143d1565b6040805160038082526080820190925291925060009190602082016060803683370190505090507f0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae981600081518110611b1e57611b1e61441f565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281600181518110611b7257611b7261441f565b6001600160a01b039283166020918202929092010152600754825191169082906002908110611ba357611ba361441f565b6001600160a01b039283166020918202929092010152611c06907f0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae9167f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f84612eba565b60006001600160a01b037f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f166338ed173984868530611c4642603c61437e565b6040518663ffffffff1660e01b8152600401611c669594939291906146f9565b6000604051808303816000875af1158015611c85573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611cad9190810190614735565b905060008160018351611cc091906144d1565b81518110611cd057611cd061441f565b60200260200101519050611d1e600860149054906101000a900460ff167f000000000000000000000000000000000000000000000000000000000000001283612c589092919063ffffffff16565b600d8054600090611d399084906001600160701b03166144e8565b82546001600160701b039182166101009390930a92830291909202199091161790555060125460ff16611d7c57600754611d7c906001600160a01b03168261319e565b60075460408051868152602081018490526001600160a01b03909216917fc003f45bc224d116b6d079100d4ab57a5b9633244c47a5a92a176c5b79a85f28910160405180910390a25050505050565b6006546001600160a01b031690565b6007546040516370a0823160e01b81526000918291611e52916001600160a01b0316906370a0823190611e119033906004016140b4565b602060405180830381865afa158015611e2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2b91906143d1565b905080841115611e60578093505b611e6c600085856135b7565b50949350505050565b60018054610a7a90614396565b6000610b443384846000611568565b6001600160a01b038116600090815260096020526040812081908190819081611ec1670de0b6b3a7640000612cc1565b6001600160a01b0388166000908152600a60205260409020549091505b8254811015611fa6576000838281548110611efb57611efb61441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff161015611f5c578054600160701b90046001600160701b0316611f3e818a61437e565b9850611f4a8185613809565b611f54908861437e565b965050611f93565b8054611f7890600160701b90046001600160701b03168861437e565b8154909750611f90906001600160701b03168661437e565b94505b5080611f9e8161469a565b915050611ede565b50600854611fe19085907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b60085490945061201e9084907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b925050509193509193565b600033612034611dcb565b6001600160a01b03161461205a5760405162461bcd60e51b8152600401610cd4906143ea565b60408051600180825281830190925260009160208083019080368337505060085482519293506001600160a01b03169183915060009061209c5761209c61441f565b6001600160a01b039283166020918202929092010152604051633111e7b360e01b81527f000000000000000000000000d784927ff2f95ba542bfc824c8a8a98f3495f6b590911690633111e7b3906120fe9084906000199030906004016147db565b6020604051808303816000875af115801561211d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214191906143d1565b91507f0000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f56001600160a01b031663787a08a66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561219e57600080fd5b505af11580156121b2573d6000803e3d6000fd5b505050507f8ca0188d9770b383d1a7a2ddfe5e0c1f029084481a53697d6c51525c47a8d88e826040516121e791815260200190565b60405180910390a15090565b6002546000908181156122185761221361220b61381e565b859084612cec565b61221a565b835b600854909150610f579082907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b600854600090612293908590600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000012612c58565b93506114ea848484613830565b336122a9611dcb565b6001600160a01b0316146122cf5760405162461bcd60e51b8152600401610cd4906143ea565b6007546001600160a01b03838116911614806122f857506008546001600160a01b038381169116145b8061230b57506001600160a01b03821630145b1561232b57816040516339b8549160e01b8152600401610cd491906140b4565b6040516370a0823160e01b81526000906001600160a01b038416906370a082319061235a9030906004016140b4565b602060405180830381865afa158015612377573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239b91906143d1565b90506123b16001600160a01b0384168383613bf4565b816001600160a01b0316836001600160a01b03167fed679328aebf74ede77ae09efcf36e90244f83643dadac1c2d9f0b21a46f6ab7836040516123f691815260200190565b60405180910390a3505050565b6000611e6c61241185612cc1565b8484613830565b33612421611dcb565b6001600160a01b0316146124475760405162461bcd60e51b8152600401610cd4906143ea565b6012805460ff1916831580159190911790915582906124635750805b1561248057600754612480906001600160a01b0316600019612d1a565b60408051831515815282151560208201527f7ac7d23f223201cd219bf262dee0820ebf6aa5ba682fbd5dd9f849bbefd05358910161155c565b336124c2611dcb565b6001600160a01b0316146124e85760405162461bcd60e51b8152600401610cd4906143ea565b601180549082905560408051828152602081018490527fcfb5a454b8aa7dc04ecb5bc1410b2a57969ca1d67f66d565196f60c6f9975404910161155c565b3361252f611dcb565b6001600160a01b0316146125555760405162461bcd60e51b8152600401610cd4906143ea565b6040516370a0823160e01b815260009030906370a082319061257b9083906004016140b4565b602060405180830381865afa158015612598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125bc91906143d1565b905060006125c98261144f565b90506125d53083613c6d565b6125de81613ccf565b600754612615906001600160a01b03167f00000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f783612eba565b600754600f54604051631ffbe7f960e01b81526001600160a01b0392831660048201526024810191909152604481018390527f00000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f790911690631ffbe7f990606401600060405180830381600087803b15801561268f57600080fd5b505af11580156126a3573d6000803e3d6000fd5b5050600e54604080516001600160701b038084168252600160701b90930490921660208301527f393c7eed3c13cd314ecf9287699a4c4737ef3422abf88199b0009cbdbc83e5c8935001905060405180910390a15050600e80546001600160e01b0319169055565b6000610c0161093f83611390565b600854600090612755908390600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000012612c58565b9150610c01826134dd565b33612769611dcb565b6001600160a01b03161461278f5760405162461bcd60e51b8152600401610cd4906143ea565b6001600160a01b0382166000908152600b60205260409020805460ff1916821580159182179092556127ce57506007546001600160a01b038381169116145b156127eb576007546127eb906001600160a01b0316600019612d1a565b5050565b6001600160a01b0381166000908152600960205260408120818061281a670de0b6b3a7640000612cc1565b6001600160a01b0386166000908152600a60205260409020549091505b83548110156128c65760008482815481106128545761285461441f565b60009182526020909120600c5491018054909250600160e01b900463ffffffff161061288a5780546001600160701b03166128a6565b80546128a690600160701b90046001600160701b031684613809565b6128b0908561437e565b93505080806128be9061469a565b915050612837565b506008546114ea9083907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b4284101561294b5760405162461bcd60e51b815260206004820152601760248201527614115493525517d11150511312539157d1561412549151604a1b6044820152606401610cd4565b6000612955610ffb565b6001600160a01b0389811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612a6e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590612aa45750886001600160a01b0316816001600160a01b0316145b612ae15760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610cd4565b6001600160a01b0390811660009081526004602090815260408083208b8516808552908352928190208a905551898152919350918a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101610f06565b33612b49611dcb565b6001600160a01b031614612b6f5760405162461bcd60e51b8152600401610cd4906143ea565b601080549082905560408051828152602081018490527f1f21432dd7b8ead64d2e7c06a74baf13783b2d2f7153f099e2c4cabc3c5dbec6910161155c565b6000610c0182612719565b33612bc1611dcb565b6001600160a01b031614612be75760405162461bcd60e51b8152600401610cd4906143ea565b6001600160a01b038116612c4c5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610cd4565b612c55816137b7565b50565b60008160ff168360ff161415612c6f575082610b44565b8160ff168360ff161015612ca357612c87838361455c565b612c9290600a614663565b612c9c9085614513565b9050610b44565b612c9c84612cb1848661455c565b612cbc90600a614663565b613d02565b6000600254600014612ce857612ce3612cd861381e565b60025484919061356f565b610c01565b5090565b828202811515841585830485141716612d0457600080fd5b6001826001830304018115150290509392505050565b6000612d24613425565b600d54909150600160701b90046001600160701b031680821115612d8c57612d4c81836144d1565b600d8054600090612d679084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b6000612d96610f17565b1115612e8157604051631a4ca37b60e21b81526001600160a01b038581166004830152602482018590523060448301526000917f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a9909116906369328dec906064016020604051808303816000875af1158015612e16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e3a91906143d1565b9050846001600160a01b03167f6407790cdabc5d219eaf901091d6beccc475533065c2fbd374c8a32b1c66795882604051612e7791815260200190565b60405180910390a2505b612e89613425565b600d80546001600160701b0392909216600160701b02600160701b600160e01b031990921691909117905550505050565b600060405163095ea7b360e01b81526001600160a01b03841660048201528260248201526000806044836000895af1915050612ef581613d34565b612f325760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610cd4565b50505050565b6040516335ea6a7560e01b81526000906001600160a01b037f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a916906335ea6a7590612f879085906004016140b4565b61018060405180830381865afa158015612fa5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc9919061483b565b5050505097505050505050505060006001600160a01b0316816001600160a01b0316141561300c5781604051630a5c5e7d60e11b8152600401610cd491906140b4565b6000600860149054906101000a900460ff1690506000836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613060573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613084919061491c565b90507f000000000000000000000000000000000000000000000000000000000000001260ff168160ff1611156130fa57604051630651982f60e11b815260ff80831660048301527f0000000000000000000000000000000000000000000000000000000000000012166024820152604401610cd4565b60ff82161580159061311257508060ff168260ff1614155b15613151576000196011541461313457601154613130908383612c58565b6011555b600019601054146131515760105461314d908383612c58565b6010555b600780546001600160a01b039586166001600160a01b031991821617909155600880549490951660ff909216600160a01b02166001600160a81b0319909316929092179190911790915550565b60006131a8613425565b600d54909150600160701b90046001600160701b031680821115613210576131d081836144d1565b600d80546000906131eb9084906001600160701b03166144e8565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b6001600160a01b0384166000908152600b602052604090205460ff1661324b57836040516386433f2b60e01b8152600401610cd491906140b4565b600d54600160e01b900463ffffffff1661327d57600d80546001600160e01b0316600160e01b4263ffffffff16021790555b6132b16001600160a01b0385167f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a985612eba565b60405163e8eda9df60e01b81526001600160a01b03858116600483015260248201859052306044830152600060648301527f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a9169063e8eda9df90608401600060405180830381600087803b15801561332857600080fd5b505af115801561333c573d6000803e3d6000fd5b50505050836001600160a01b03167f3b1270fa6f77c9af94834571cf5274944e8712de6cebea9ec3d8b3452c0533088460405161337b91815260200190565b60405180910390a2612e89613425565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516133bd9190614939565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6008546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061345a9030906004016140b4565b602060405180830381865afa158015613477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349b91906143d1565b6008549091506134d7908290600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000012612c58565b91505090565b6000806134e861381e565b6002549091508115806134f9575080155b61350d5761350884828461356f565b610f57565b509192915050565b8060026000828254613527919061437e565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481526000805160206149ea83398151915291015b60405180910390a35050565b82820281151584158583048514171661358757600080fd5b0492915050565b600081831161359e576000610b44565b610b4482846144d1565b6000818310610c2c5781610b44565b601254600090819060ff16156135e057604051632f22819760e11b815260040160405180910390fd5b600084116135f9576135f185612bad565b935083613606565b613602846121f3565b9450845b50836136255760405163df46449f60e01b815260040160405180910390fd5b61362e83611390565b851115613661578461363f84611390565b6040516323dc290560e21b815260048101929092526024820152604401610cd4565b600754613679906001600160a01b0316333088613d7b565b6136838385613515565b6001600160a01b038316600090815260096020526040908190208151606081019092526008549091829181906136e5908a90600160a01b900460ff167f0000000000000000000000000000000000000000000000000000000000000012612c58565b6001600160701b03908116825288811660208084019190915263ffffffff42811660409485015285546001810187556000968752958290208551960180548684015196860151909216600160e01b026001600160e01b03968516600160701b026001600160e01b0319909316979094169690961717939093161790925560075482518981529182018890526001600160a01b03908116929087169133917f5fe47ed6d4225326d3303476197d782ded5a4e9c14f479dc9ec4992af4e85d59910160405180910390a45093949293505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000610b448383670de0b6b3a764000061356f565b6000613828613e0f565b610a5e613425565b6001600160a01b03811660009081526003602052604081205481906138685760405163df46449f60e01b815260040160405180910390fd5b846138865760405163332215e760e11b815260040160405180910390fd5b6001600160a01b038316600090815260096020526040812086826138b1670de0b6b3a7640000612cc1565b6001600160a01b0388166000908152600a60205260409020549091505b8354811015613ab35760008482815481106138eb576138eb61441f565b600091825260208220600c5491018054909350600160e01b900463ffffffff161090816139225782546001600160701b031661393e565b825461393e90600160701b90046001600160701b031686613809565b9050600061394c87836135a8565b845490915060009061396f90600160701b90046001600160701b03168385612cec565b9050831561398c578454600160701b600160e01b031685556139ce565b8454829086906000906139a99084906001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b031602179055505b845481908690600e906139f2908490600160701b90046001600160701b0316614672565b92506101000a8154816001600160701b0302191690836001600160701b03160217905550808a613a22919061437e565b9950613a2e82896144d1565b8954909850613a3f906001906144d1565b861480613a4a575087155b15613a9b578454600160701b90046001600160701b0316613a7557613a7086600161437e565b613a77565b855b6001600160a01b038e166000908152600a602052604090205550613ab39350505050565b50505050508080613aab9061469a565b9150506138ce565b50336001600160a01b03881614613b22576001600160a01b03871660009081526004602090815260408083203384529091529020546000198114613b2057613afb85826144d1565b6001600160a01b03891660009081526004602090815260408083203384529091529020555b505b613b2c8785613c6d565b613b36828a6144d1565b600854909950613b73908a907f000000000000000000000000000000000000000000000000000000000000001290600160a01b900460ff16612c58565b9850613b7e89613ccf565b600754613b95906001600160a01b0316898b613bf4565b600754604080518b8152602081018790526001600160a01b03928316928a811692908c16917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a450969791965090945050505050565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af1915050613c2f81613d34565b612f325760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610cd4565b6001600160a01b03821660009081526003602052604081208054839290613c959084906144d1565b90915550506002805482900390556040518181526000906001600160a01b038416906000805160206149ea83398151915290602001613563565b6000613cd9610c33565b9050808211156127eb576007546127eb906001600160a01b0316613cfd83856144d1565b612d1a565b6000613d0e82846149d5565b15613d1a576001613d1d565b60005b60ff16613d2a8385614548565b610b44919061437e565b60003d82613d4657806000803e806000fd5b8060208114613d5e578015613d6f5760009250613d74565b816000803e60005115159250613d74565b600192505b5050919050565b60006040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260008060648360008a5af1915050613dc581613d34565b613e085760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610cd4565b5050505050565b6007546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061345a9030906004016140b4565b600060208083528351808285015260005b81811015613e7157858101830151858201604001528201613e55565b81811115613e83576000604083870101525b50601f01601f1916929092016040019392505050565b600060208284031215613eab57600080fd5b5035919050565b6001600160a01b0381168114612c5557600080fd5b60008060408385031215613eda57600080fd5b8235613ee581613eb2565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b60405290565b6040516080810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b6040516060810167ffffffffffffffff81118282101715613f2d57613f2d613ef3565b604051601f8201601f1916810167ffffffffffffffff81118282101715613fa257613fa2613ef3565b604052919050565b60008060006102c08486031215613fc057600080fd5b601f8581860112613fd057600080fd5b613fd8613f09565b80610120870188811115613feb57600080fd5b875b8181101561400e57803561400081613eb2565b845260209384019301613fed565b508196508861013f89011261402257600080fd5b61402a613f33565b92508291506102a088018981111561404157600080fd5b808210156140a45789858301126140585760008081fd5b614060613f56565b80606084018c8111156140735760008081fd5b845b8181101561408d578035845260209384019301614075565b505085525060209093019260609190910190614041565b9699919850509435955050505050565b6001600160a01b0391909116815260200190565b6000806000606084860312156140dd57600080fd5b83356140e881613eb2565b925060208401356140f881613eb2565b929592945050506040919091013590565b60006020828403121561411b57600080fd5b8135610b4481613eb2565b602080825282518282018190526000919060409081850190868401855b8281101561418457815180516001600160701b03908116865287820151168786015285015163ffffffff168585015260609093019290850190600101614143565b5091979650505050505050565b600080604083850312156141a457600080fd5b8235915060208301356141b681613eb2565b809150509250929050565b803580151581146141d157600080fd5b919050565b600080600080608085870312156141ec57600080fd5b84356141f781613eb2565b9350602085013561420781613eb2565b92506040850135915061421c606086016141c1565b905092959194509250565b60008060006060848603121561423c57600080fd5b83359250602084013561424e81613eb2565b9150604084013561425e81613eb2565b809150509250925092565b6000806040838503121561427c57600080fd5b823561428781613eb2565b915060208301356141b681613eb2565b600080604083850312156142aa57600080fd5b6142b3836141c1565b91506142c1602084016141c1565b90509250929050565b600080604083850312156142dd57600080fd5b82356142b381613eb2565b60ff81168114612c5557600080fd5b600080600080600080600060e0888a03121561431257600080fd5b873561431d81613eb2565b9650602088013561432d81613eb2565b95506040880135945060608801359350608088013561434b816142e8565b9699959850939692959460a0840135945060c09093013592915050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561439157614391614368565b500190565b600181811c908216806143aa57607f821691505b602082108114156143cb57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156143e357600080fd5b5051919050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6102e08101818660005b60098110156144675781516001600160a01b031683526020928301929091019060010161443f565b50505061012082018560005b60048110156144ba5781518360005b60038110156144a1578251825260209283019290910190600101614482565b5050506060929092019160209190910190600101614473565b5050506102a08201939093526102c0015292915050565b6000828210156144e3576144e3614368565b500390565b60006001600160701b0382811684821680830382111561450a5761450a614368565b01949350505050565b600081600019048311821515161561452d5761452d614368565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261455757614557614532565b500490565b600060ff821660ff84168082101561457657614576614368565b90039392505050565b600181815b808511156145ba5781600019048211156145a0576145a0614368565b808516156145ad57918102915b93841c9390800290614584565b509250929050565b6000826145d157506001610c01565b816145de57506000610c01565b81600181146145f457600281146145fe5761461a565b6001915050610c01565b60ff84111561460f5761460f614368565b50506001821b610c01565b5060208310610133831016604e8410600b841016171561463d575081810a610c01565b614647838361457f565b806000190482111561465b5761465b614368565b029392505050565b6000610b4460ff8416836145c2565b60006001600160701b038381169083168181101561469257614692614368565b039392505050565b60006000198214156146ae576146ae614368565b5060010190565b600081518084526020808501945080840160005b838110156146ee5781516001600160a01b0316875295820195908201906001016146c9565b509495945050505050565b85815284602082015260a06040820152600061471860a08301866146b5565b6001600160a01b0394909416606083015250608001529392505050565b6000602080838503121561474857600080fd5b825167ffffffffffffffff8082111561476057600080fd5b818501915085601f83011261477457600080fd5b81518181111561478657614786613ef3565b8060051b9150614797848301613f79565b81815291830184019184810190888411156147b157600080fd5b938501935b838510156147cf578451825293850193908501906147b6565b98975050505050505050565b6060815260006147ee60608301866146b5565b6020830194909452506001600160a01b0391909116604090910152919050565b80516001600160801b03811681146141d157600080fd5b80516141d181613eb2565b80516141d1816142e8565b6000806000806000806000806000806000806101808d8f03121561485e57600080fd5b8c519b5061486e60208e0161480e565b9a5061487c60408e0161480e565b995061488a60608e0161480e565b985061489860808e0161480e565b97506148a660a08e0161480e565b965060c08d015164ffffffffff811681146148c057600080fd5b95506148ce60e08e01614825565b94506148dd6101008e01614825565b93506148ec6101208e01614825565b92506148fb6101408e01614825565b915061490a6101608e01614830565b90509295989b509295989b509295989b565b60006020828403121561492e57600080fd5b8151610b44816142e8565b600080835481600182811c91508083168061495557607f831692505b602080841082141561497557634e487b7160e01b86526022600452602486fd5b818015614989576001811461499a576149c7565b60ff198616895284890196506149c7565b60008a81526020902060005b868110156149bf5781548b8201529085019083016149a6565b505084890196505b509498975050505050505050565b6000826149e4576149e4614532565b50069056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa164736f6c634300080b000a

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

000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000014000000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd7000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a9000000000000000000000000d784927ff2f95ba542bfc824c8a8a98f3495f6b500000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f70000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f50000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae9000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000008000000000000000000000000056fd409e1d7a124bd7017459dfea2f387b6d5cd0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000956f47f50a910163d8bf957cf5846d573e7f87ca000000000000000000000000853d955acef822db058eb8505911ed77f175b99e00000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f510000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1

-----Decoded View---------------
Arg [0] : _asset (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [1] : _approvedPositions (address[]): 0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd,0x4Fabb145d64652a948d72533023f6E7A623C7C53,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x6B175474E89094C44Da98b954EedeAC495271d0F,0x956F47F50A910163D8BF957Cf5846D573E7f87CA,0x853d955aCEf822Db058eb8505911ED77F175b99e,0x57Ab1ec28D129707052df4dF418D58a2D46d5f51,0x8E870D67F660D95d5be530380D0eC0bd388289E1
Arg [2] : _curveRegistryExchange (address): 0x81C46fECa27B31F3ADC2b91eE4be9717d1cd3DD7
Arg [3] : _sushiswapRouter (address): 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
Arg [4] : _lendingPool (address): 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9
Arg [5] : _incentivesController (address): 0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5
Arg [6] : _gravityBridge (address): 0x69592e6f9d21989a043646fE8225da2600e5A0f7
Arg [7] : _stkAAVE (address): 0x4da27a545c0c5B758a6BA100e3a049001de870f5
Arg [8] : _AAVE (address): 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9
Arg [9] : _WETH (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
19 Constructor Arguments found :
Arg [0] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [2] : 00000000000000000000000081c46feca27b31f3adc2b91ee4be9717d1cd3dd7
Arg [3] : 000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
Arg [4] : 0000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a9
Arg [5] : 000000000000000000000000d784927ff2f95ba542bfc824c8a8a98f3495f6b5
Arg [6] : 00000000000000000000000069592e6f9d21989a043646fe8225da2600e5a0f7
Arg [7] : 0000000000000000000000004da27a545c0c5b758a6ba100e3a049001de870f5
Arg [8] : 0000000000000000000000007fc66500c84a76ad7e9c93437bfc5ac33e2ddae9
Arg [9] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [11] : 000000000000000000000000056fd409e1d7a124bd7017459dfea2f387b6d5cd
Arg [12] : 0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53
Arg [13] : 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7
Arg [14] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Arg [15] : 000000000000000000000000956f47f50a910163d8bf957cf5846d573e7f87ca
Arg [16] : 000000000000000000000000853d955acef822db058eb8505911ed77f175b99e
Arg [17] : 00000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f51
Arg [18] : 0000000000000000000000008e870d67f660d95d5be530380d0ec0bd388289e1


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.