ETH Price: $3,414.41 (+3.59%)

Contract

0x0843359caE1187b432eEb26E1B40c3a2B2374D7E
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040183689782023-10-17 8:44:11403 days ago1697532251IN
 Create: Native20
0 ETH0.033592448.3092693

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Native20

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 36 : Native20.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/types/mapping.sol";
import "utils.sol/types/string.sol";

import "utils.sol/Implementation.sol";
import "utils.sol/Initializable.sol";

import "./MultiPool20.sol";
import "./interfaces/INative20.sol";

/// @title Native20 (V1)
/// @author 0xvv @ Kiln
/// @notice This contract allows users to stake any amount of ETH in the vPool(s)
/// @notice Users are given non transferable ERC-20 type shares to track their stake
contract Native20 is MultiPool20, INative20, Implementation, Initializable {
    using LMapping for types.Mapping;
    using LString for types.String;
    using LUint256 for types.Uint256;

    /// @dev The name of the shares.
    /// @dev Slot: keccak256(bytes("native20.1.name")) - 1
    types.String internal constant $name = types.String.wrap(0xeee152275d096301850a53ae85c6991c818bc6bac8a2174c268aa94ed7cf06f1);

    /// @dev The symbol of the shares.
    /// @dev Slot: keccak256(bytes("native20.1.symbol")) - 1
    types.String internal constant $symbol = types.String.wrap(0x4a8b3e24ebc795477af927068865c6fcc26e359a994edca2492e515a46aad711);

    /// @inheritdoc INative20
    function initialize(Native20Configuration calldata args) external init(0) {
        $name.set(args.name);
        emit SetName(args.name);
        $symbol.set(args.symbol);
        emit SetSymbol(args.symbol);

        Administrable._setAdmin(args.admin);

        if (args.pools.length == 0) {
            revert EmptyPoolList();
        }
        if (args.pools.length != args.poolFees.length) {
            revert UnequalLengths(args.pools.length, args.poolFees.length);
        }
        for (uint256 i = 0; i < args.pools.length;) {
            _addPool(args.pools[i], args.poolFees[i]);
            unchecked {
                i++;
            }
        }
        _setPoolPercentages(args.poolPercentages);
        _initFeeDispatcher(args.commissionRecipients, args.commissionDistribution);
        _setMaxCommission(args.maxCommissionBps);
        _setMonoTicketThreshold(args.monoTicketThreshold);
    }

    /// @inheritdoc INative20
    function name() external view returns (string memory) {
        return string(abi.encodePacked($name.get()));
    }

    /// @inheritdoc INative20
    function symbol() external view returns (string memory) {
        return string(abi.encodePacked($symbol.get()));
    }

    /// @inheritdoc INative20
    function decimals() external view virtual override returns (uint8) {
        return 18;
    }

    /// @inheritdoc INative20
    function balanceOf(address account) external view virtual returns (uint256) {
        return _balanceOf(account);
    }

    /// @inheritdoc INative20
    function balanceOfUnderlying(address account) external view virtual returns (uint256) {
        return _balanceOfUnderlying(account);
    }

    /// @inheritdoc INative20
    function totalSupply() external view virtual returns (uint256) {
        return _totalSupply();
    }

    /// @inheritdoc INative20
    function totalUnderlyingSupply() external view virtual returns (uint256) {
        return _totalUnderlyingSupply();
    }

    /// @inheritdoc INative20
    function stake() external payable {
        LibSanitize.notNullValue(msg.value);
        _stake(msg.value);
    }
}

File 2 of 36 : mapping.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

library LMapping {
    // slither-disable-next-line dead-code
    function get(types.Mapping position) internal pure returns (mapping(uint256 => uint256) storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

File 3 of 36 : string.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

library LString {
    struct StringStorage {
        string value;
    }

    // slither-disable-next-line dead-code
    function get(types.String position) internal view returns (string memory) {
        StringStorage storage ss;

        // slither-disable-next-line assembly
        assembly {
            ss.slot := position
        }

        return ss.value;
    }

    // slither-disable-next-line dead-code
    function set(types.String position, string memory value) internal {
        StringStorage storage ss;

        // slither-disable-next-line assembly
        assembly {
            ss.slot := position
        }

        ss.value = value;
    }
}

File 4 of 36 : Implementation.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types/uint256.sol";

/// @title Implementation
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice This contracts must be used on all implementation contracts. It ensures that the initializers are only callable through the proxy.
///         This will brick the implementation and make it unusable directly without using delegatecalls.
abstract contract Implementation {
    using LUint256 for types.Uint256;

    /// @dev The version number in storage in the initializable contract.
    /// @dev Slot: keccak256(bytes("initializable.version"))) - 1
    types.Uint256 internal constant $initializableVersion =
        types.Uint256.wrap(0xc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a76);

    constructor() {
        $initializableVersion.set(type(uint256).max);
    }
}

File 5 of 36 : Initializable.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types/uint256.sol";

/// @title Initializable
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice This contracts helps upgradeable contracts handle an internal
///         version value to prevent initializer replays.
abstract contract Initializable {
    using LUint256 for types.Uint256;

    /// @notice The version has been initialized.
    /// @param version The version number initialized
    /// @param cdata The calldata used for the call
    event Initialized(uint256 version, bytes cdata);

    /// @notice The init modifier has already been called on the given version number.
    /// @param version The provided version number
    /// @param currentVersion The stored version number
    error AlreadyInitialized(uint256 version, uint256 currentVersion);

    /// @dev The version number in storage.
    /// @dev Slot: keccak256(bytes("initializable.version"))) - 1
    types.Uint256 internal constant $version =
        types.Uint256.wrap(0xc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a76);

    /// @dev The modifier to use on initializers.
    /// @dev Do not provide _version dynamically, make sure the value is hard-coded each
    ///      time the modifier is used.
    /// @param _version The version to initialize
    // slither-disable-next-line incorrect-modifier
    modifier init(uint256 _version) {
        if (_version == $version.get()) {
            $version.set(_version + 1);
            emit Initialized(_version, msg.data);
            _;
        } else {
            revert AlreadyInitialized(_version, $version.get());
        }
    }
}

File 6 of 36 : MultiPool20.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "vsuite/ctypes/ctypes.sol";
import "vsuite/ctypes/approvals_mapping.sol";

import "./MultiPool.sol";
import "./interfaces/IMultiPool20.sol";
import "./victypes/victypes.sol";
import "./victypes/balance.sol";

uint256 constant MIN_SUPPLY = 1e14; // If there is only dust in the pool, we mint 1:1
uint256 constant COMMISSION_MAX = 10; // 0.1% / 10 bps ~= 12 days of accrued commission at 3% GRR

/// @title MultiPool-20 (v1)
/// @author 0xvv @ Kiln
/// @notice This contract contains the internal logic for an ERC-20 token based on one or multiple pools.
abstract contract MultiPool20 is MultiPool, IMultiPool20 {
    using LArray for types.Array;
    using LMapping for types.Mapping;
    using LUint256 for types.Uint256;
    using LBalance for victypes.BalanceMapping;
    using LApprovalsMapping for ctypes.ApprovalsMapping;
    using CUint256 for uint256;

    using CBool for bool;

    /// @dev The total supply of ERC 20.
    /// @dev Slot: keccak256(bytes("multiPool20.1.totalSupply")) - 1
    types.Uint256 internal constant $totalSupply = types.Uint256.wrap(0xb24a0f21470b6927dcbaaf5b1f54865bd687f4a2ce4c43edf1e20339a4c05bae);

    /// @dev The list containing the percentages of ETH to route to each pool, in basis points, must add up to 10 000.
    /// @dev Slot: keccak256(bytes("multiPool20.1.poolRoutingList")) - 1
    types.Array internal constant $poolRoutingList = types.Array.wrap(0x3803482dd7707d12238e38a3b1b5e55fa6e13d81c36ce29ec5c267cc02c53fe3);

    /// @dev Stores the balances : mapping(address => uint256).
    /// @dev Slot: keccak256(bytes("multiPool20.1.balances")) - 1
    victypes.BalanceMapping internal constant $balances =
        victypes.BalanceMapping.wrap(0x4f74125ce1aafb5d1699fc2e5e8f96929ff1a99170dc9bda82c8944acc5c7286);

    /// @dev Stores the approvals
    /// @dev Type: mapping(address => mapping(address => bool).
    /// @dev Slot: keccak256(bytes("multiPool20.1.approvals")) - 1
    ctypes.ApprovalsMapping internal constant $approvals =
        ctypes.ApprovalsMapping.wrap(0xebc1e0a04bae59eb2e2b17f55cd491aec28c349ae4f6b6fe9be28a72f9c6b202);

    /// @dev The threshold below which we try to issue only one exit ticket
    /// @dev Slot: keccak256(bytes("multiPool20.1.monoTicketThreshold")) - 1
    types.Uint256 internal constant $monoTicketThreshold =
        types.Uint256.wrap(0x900053b761278bb5de4eeaea5ed9000b89943edad45dcf64a9dab96d0ce29c2e);

    /// @inheritdoc IMultiPool20
    function setPoolPercentages(uint256[] calldata split) external onlyAdmin {
        _setPoolPercentages(split);
    }

    /// @notice Sets the threshold below which we try to issue only one exit ticket
    /// @param minTicketEthValue The threshold
    function setMonoTicketThreshold(uint256 minTicketEthValue) external onlyAdmin {
        _setMonoTicketThreshold(minTicketEthValue);
    }

    /// @inheritdoc IMultiPool20
    function requestExit(uint256 amount) external virtual {
        _requestExit(amount);
    }

    /// @inheritdoc IMultiPool20
    function rate() external view returns (uint256) {
        uint256 currentTotalSupply = _totalSupply();
        return currentTotalSupply > 0 ? LibUint256.mulDiv(_totalUnderlyingSupply(), 1e18, currentTotalSupply) : 1e18;
    }

    /// Private functions

    /// @dev Internal function to requestExit
    /// @param amount The amount of shares to exit
    // slither-disable-next-line reentrancy-events
    function _requestExit(uint256 amount) internal {
        uint256 totalSupply = $totalSupply.get();
        uint256 totalUnderlyingSupply = _totalUnderlyingSupply();
        _burn(msg.sender, amount);
        uint256 ethValue = LibUint256.mulDiv(amount, totalUnderlyingSupply, totalSupply);
        uint256 poolCount_ = $poolCount.get();
        // Early return in case of mono pool operation
        if (poolCount_ == 1) {
            PoolExitDetails[] memory detail = new PoolExitDetails[](1);
            _sendToExitQueue(0, ethValue, detail[0]);
            _checkCommissionRatio(0);
            emit Exit(msg.sender, uint128(amount), detail);
            return;
        }
        uint256[] memory splits = $poolRoutingList.toUintA();
        // If the amount is below the set threshold we exit via the most imabalanced pool to print only 1 ticket
        if (ethValue < $monoTicketThreshold.get()) {
            int256 maxImbalance = 0;
            uint256 exitPoolId = 0;
            for (uint256 id = 0; id < poolCount_;) {
                uint256 expectedValue = LibUint256.mulDiv(totalUnderlyingSupply, splits[id], LibConstant.BASIS_POINTS_MAX);
                uint256 poolValue = _ethAfterCommission(id);
                int256 imbalance = int256(poolValue) - int256(expectedValue);
                if (poolValue >= ethValue && imbalance > maxImbalance) {
                    maxImbalance = imbalance;
                    exitPoolId = id;
                }
                unchecked {
                    id++;
                }
            }
            if (maxImbalance > 0) {
                PoolExitDetails[] memory detail = new PoolExitDetails[](1);
                _sendToExitQueue(exitPoolId, ethValue, detail[0]);
                _checkCommissionRatio(exitPoolId);
                emit Exit(msg.sender, uint128(amount), detail);
                return;
            }
        }
        // If the the amount is over the threshold or no pool has enough value to cover the exit
        // We exit proportionally to maintain the balance
        PoolExitDetails[] memory details = new PoolExitDetails[](poolCount_);
        for (uint256 id = 0; id < poolCount_;) {
            uint256 ethForPool = LibUint256.mulDiv(ethValue, splits[id], LibConstant.BASIS_POINTS_MAX);
            if (ethForPool > 0) _sendToExitQueue(id, ethForPool, details[id]);
            _checkCommissionRatio(id);
            unchecked {
                id++;
            }
        }
        emit Exit(msg.sender, uint128(amount), details);
    }

    /// @dev Internal function to exit the commission shares if needed
    /// @param id The pool id
    function _checkCommissionRatio(uint256 id) internal {
        // If the commission shares / all shares ratio go over the limit we exit them
        if (_poolSharesOfIntegrator(id) > LibUint256.mulDiv($poolShares.get()[id], COMMISSION_MAX, LibConstant.BASIS_POINTS_MAX)) {
            _exitCommissionShares(id);
        }
    }

    /// @dev Utility function to send a given ETH amount of shares to the exit queue of a pool
    // slither-disable-next-line calls-loop
    function _sendToExitQueue(uint256 poolId, uint256 ethAmount, PoolExitDetails memory details) internal {
        IvPool pool = _getPool(poolId);
        uint256 shares = LibUint256.mulDiv(ethAmount, pool.totalSupply(), pool.totalUnderlyingSupply());
        uint256 stakedValueBefore = _stakedEthValue(poolId);
        details.exitedPoolShares = uint128(shares);
        details.poolId = uint128(poolId);
        _sendSharesToExitQueue(poolId, shares, pool, msg.sender);
        $exitedEth.get()[poolId] += stakedValueBefore - _stakedEthValue(poolId);
    }

    /// @dev Internal function to stake in one or more pools with arbitrary amounts to each one
    /// @param totalAmount The amount of ETH to stake
    // slither-disable-next-line reentrancy-events,unused-return,dead-code
    function _stake(uint256 totalAmount) internal notPaused returns (bool) {
        uint256[] memory splits = $poolRoutingList.toUintA();
        PoolStakeDetails[] memory stakeDetails = new PoolStakeDetails[](splits.length);
        uint256 tokensBoughtTotal = 0;
        for (uint256 id = 0; id < $poolCount.get();) {
            if (splits[id] > 0) {
                stakeDetails[id].poolId = uint128(id);
                uint256 remainingEth = LibUint256.mulDiv(totalAmount, splits[id], LibConstant.BASIS_POINTS_MAX);
                _checkPoolIsEnabled(id);
                IvPool pool = _getPool(id);
                uint256 totalSupply = _totalSupply(); // we can use these values because the ratio of shares to underlying is constant in this function
                uint256 totalUnderlyingSupply = _totalUnderlyingSupply();
                if (totalSupply < MIN_SUPPLY) {
                    $injectedEth.get()[id] += remainingEth;
                    uint256 sharesAcquired = pool.deposit{value: remainingEth}();
                    tokensBoughtTotal += sharesAcquired;
                    _mint(msg.sender, sharesAcquired);
                    stakeDetails[id].ethToPool = uint128(remainingEth);
                    stakeDetails[id].pSharesFromPool = uint128(sharesAcquired);
                } else {
                    uint256 comOwed = _integratorCommissionOwed(id);
                    uint256 tokensBoughtPool = 0;
                    // If there is enough commission we sell it first
                    // This avoids wasting gas to sell infinitesimal amounts of commission + a potential DoS vector
                    if (comOwed > MIN_COMMISSION_TO_SELL) {
                        uint256 ethForCommission = LibUint256.min(comOwed, remainingEth);
                        remainingEth -= ethForCommission;
                        uint256 pSharesBought = LibUint256.mulDiv(
                            ethForCommission, $poolShares.get()[id] - _poolSharesOfIntegrator(id), _ethAfterCommission(id)
                        );
                        $commissionPaid.get()[id] += ethForCommission;
                        stakeDetails[id].ethToIntegrator = uint128(ethForCommission);
                        stakeDetails[id].pSharesFromIntegrator = uint128(pSharesBought);
                        emit CommissionSharesSold(pSharesBought, id, ethForCommission);
                        uint256 tokensAcquired = LibUint256.mulDiv(ethForCommission, totalSupply, totalUnderlyingSupply);
                        if (tokensAcquired == 0) revert ZeroSharesMint();
                        tokensBoughtPool += tokensAcquired;
                    }
                    if (remainingEth > 0) {
                        $injectedEth.get()[id] += remainingEth;
                        uint256 pShares = pool.deposit{value: remainingEth}();
                        uint256 tokensAcquired = LibUint256.mulDiv(remainingEth, totalSupply, totalUnderlyingSupply);
                        if (tokensAcquired == 0) revert ZeroSharesMint();
                        stakeDetails[id].ethToPool += uint128(remainingEth);
                        stakeDetails[id].pSharesFromPool += uint128(pShares);
                        tokensBoughtPool += tokensAcquired;
                    }
                    _mint(msg.sender, tokensBoughtPool);
                    tokensBoughtTotal += tokensBoughtPool;
                }
            }
            unchecked {
                id++;
            }
        }
        emit Stake(msg.sender, uint128(totalAmount), uint128(tokensBoughtTotal), stakeDetails);
        return true;
    }

    /// @dev Internal function to set the pool percentages
    /// @param percentages The new percentages
    function _setPoolPercentages(uint256[] calldata percentages) internal {
        if (percentages.length != $poolCount.get()) {
            revert UnequalLengths(percentages.length, $poolCount.get());
        }
        uint256 total = 0;
        $poolRoutingList.del();
        uint256[] storage percentagesList = $poolRoutingList.toUintA();
        for (uint256 i = 0; i < percentages.length;) {
            bool enabled = $poolActivation.get()[i].toBool();
            uint256 percentage = percentages[i];
            if (!enabled && percentage != 0) {
                revert NonZeroPercentageOnDeactivatedPool(i);
            } else {
                total += percentages[i];
                percentagesList.push(percentages[i]);
            }

            unchecked {
                i++;
            }
        }
        if (total != LibConstant.BASIS_POINTS_MAX) {
            revert LibErrors.InvalidBPSValue();
        }

        emit SetPoolPercentages(percentages);
    }

    /// @inheritdoc IMultiPool20
    function setPoolActivation(uint256 poolId, bool status, uint256[] calldata newPoolPercentages) external onlyAdmin {
        $poolActivation.get()[poolId] = status.v();
        _setPoolPercentages(newPoolPercentages);
    }

    /// @dev Internal function to retrieve the balance of a given account
    /// @param account The account to retrieve the balance of
    // slither-disable-next-line dead-code
    function _balanceOf(address account) internal view returns (uint256) {
        return $balances.get()[account];
    }

    /// @dev Internal function to retrieve the balance of a given account in underlying
    /// @param account The account to retrieve the balance of in underlying
    // slither-disable-next-line dead-code
    function _balanceOfUnderlying(address account) internal view returns (uint256) {
        uint256 tUnderlyingSupply = _totalUnderlyingSupply();
        uint256 tSupply = _totalSupply();
        if (tUnderlyingSupply == 0 || tSupply == 0) {
            return 0;
        }
        return LibUint256.mulDiv($balances.get()[account], tUnderlyingSupply, tSupply);
    }

    /// @dev Internal function retrieve the total underlying supply
    // slither-disable-next-line naming-convention
    function _totalUnderlyingSupply() internal view returns (uint256) {
        uint256 ethValue = 0;
        for (uint256 i = 0; i < $poolCount.get();) {
            unchecked {
                ethValue += _ethAfterCommission(i);
                i++;
            }
        }
        return ethValue;
    }

    /// @dev Internal function to retrieve the total supply
    // slither-disable-next-line naming-convention
    function _totalSupply() internal view returns (uint256) {
        return $totalSupply.get();
    }

    /// @dev Internal function to transfer tokens from one account to another
    /// @param from The account to transfer from
    /// @param to The account to transfer to
    /// @param amount The amount to transfer
    // slither-disable-next-line dead-code
    function _transfer(address from, address to, uint256 amount) internal virtual {
        uint256 fromBalance = $balances.get()[from];
        if (amount > fromBalance) {
            revert InsufficientBalance(amount, fromBalance);
        }
        unchecked {
            $balances.get()[from] = fromBalance - amount;
        }
        $balances.get()[to] += amount;

        emit Transfer(from, to, amount);
    }

    /// @dev Internal function to retrieve the allowance of a given spender
    /// @param owner The owner of the allowance
    /// @param spender The spender of the allowance
    // slither-disable-next-line dead-code
    function _allowance(address owner, address spender) internal view returns (uint256) {
        return $approvals.get()[owner][spender];
    }

    /// @dev Internal function to approve a spender
    /// @param owner The owner of the allowance
    /// @param spender The spender of the allowance
    /// @param amount The amount to approve
    // slither-disable-next-line dead-code
    function _approve(address owner, address spender, uint256 amount) internal {
        $approvals.get()[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /// @dev Internal function to transfer tokens from one account to another
    /// @param spender The spender of the allowance
    /// @param from The account to transfer from
    /// @param to The account to transfer to
    /// @param amount The amount to transfer
    // slither-disable-next-line dead-code
    function _transferFrom(address spender, address from, address to, uint256 amount) internal virtual {
        uint256 currentAllowance = $approvals.get()[from][spender];
        if (amount > currentAllowance) {
            revert InsufficientAllowance(amount, currentAllowance);
        }
        unchecked {
            $approvals.get()[from][spender] = currentAllowance - amount;
        }
        _transfer(from, to, amount);
    }

    /// @dev Internal function for minting
    /// @param account The address to mint to
    /// @param amount The amount to mint
    // slither-disable-next-line dead-code
    function _mint(address account, uint256 amount) internal {
        $totalSupply.set($totalSupply.get() + amount);
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, checked above
            $balances.get()[account] += amount;
        }
        emit Transfer(address(0), account, amount);
    }

    /// @dev Internal function to burn tokens
    /// @param account The account to burn from
    /// @param amount The amount to burn
    // slither-disable-next-line dead-code
    function _burn(address account, uint256 amount) internal {
        uint256 accountBalance = $balances.get()[account];
        if (amount > accountBalance) {
            revert InsufficientBalance(amount, accountBalance);
        }
        $totalSupply.set($totalSupply.get() - amount);
        unchecked {
            $balances.get()[account] = accountBalance - amount;
        }
        emit Transfer(account, address(0), amount);
    }

    /// @dev Internal function to set the mono ticket threshold
    /// @param minTicketEthValue The minimum ticket value
    function _setMonoTicketThreshold(uint256 minTicketEthValue) internal {
        $monoTicketThreshold.set(minTicketEthValue);
    }
}

File 7 of 36 : INative20.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

/// @notice Configuration parameters for the Native20 contract.
/// @param admin The address of the admin.
/// @param name ERC-20 style display name.
/// @param symbol ERC-20 style display symbol.
/// @param pools List of pool addresses.
/// @param poolFees List of fee for each pool, in basis points.
/// @param commissionRecipients List of recipients among which the withdrawn fees are shared.
/// @param commissionDistribution Share of each fee recipient, in basis points, must add up to 10 000.
/// @param poolPercentages The amount of ETH to route to each pool when staking, in basis points, must add up to 10 000.
struct Native20Configuration {
    string name;
    string symbol;
    address admin;
    address[] pools;
    uint256[] poolFees;
    address[] commissionRecipients;
    uint256[] commissionDistribution;
    uint256[] poolPercentages;
    uint256 maxCommissionBps;
    uint256 monoTicketThreshold;
}

/// @title Native20 (V1) Interface
/// @author 0xvv @ Kiln
/// @notice This contract allows users to stake any amount of ETH in the vPool(s).
///         Users are given non transferable ERC-20 type shares to track their stake.
interface INative20 {
    /// @notice Initializes the contract with the given parameters.
    /// @param args The initialization arguments.
    function initialize(Native20Configuration calldata args) external;

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token, usually a shorter version of the name.
    function symbol() external view returns (string memory);

    /// @notice Returns the number of decimals used to get its user representation.
    function decimals() external view returns (uint8);

    /// @notice Returns the total amount of staking shares.
    /// @return Total amount of shares.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of ETH owned by the users in the pool(s).
    /// @return Total amount of shares.
    function totalUnderlyingSupply() external view returns (uint256);

    /// @notice Returns the amount of staking shares for an account.
    /// @param account The address of the account.
    /// @return amount of staking shares.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the ETH value of the account balance.
    /// @param account The address of the account.
    /// @return amount of ETH.
    function balanceOfUnderlying(address account) external view returns (uint256);

    /// @notice Function to stake ETH.
    function stake() external payable;
}

File 8 of 36 : types.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

/// @dev Library holding bytes32 custom types
// slither-disable-next-line naming-convention
library types {
    type Uint256 is bytes32;
    type Address is bytes32;
    type Bytes32 is bytes32;
    type Bool is bytes32;
    type String is bytes32;
    type Mapping is bytes32;
    type Array is bytes32;
}

File 9 of 36 : uint256.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

library LUint256 {
    // slither-disable-next-line dead-code
    function get(types.Uint256 position) internal view returns (uint256 data) {
        // slither-disable-next-line assembly
        assembly {
            data := sload(position)
        }
    }

    // slither-disable-next-line dead-code
    function set(types.Uint256 position, uint256 data) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, data)
        }
    }

    // slither-disable-next-line dead-code
    function del(types.Uint256 position) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, 0)
        }
    }
}

library CUint256 {
    // slither-disable-next-line dead-code
    function toBytes32(uint256 val) internal pure returns (bytes32) {
        return bytes32(val);
    }

    // slither-disable-next-line dead-code
    function toAddress(uint256 val) internal pure returns (address) {
        return address(uint160(val));
    }

    // slither-disable-next-line dead-code
    function toBool(uint256 val) internal pure returns (bool) {
        return (val & 1) == 1;
    }
}

File 10 of 36 : ctypes.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/libs/LibPublicKey.sol";
import "utils.sol/libs/LibSignature.sol";

/// @title Custom Types
// slither-disable-next-line naming-convention
library ctypes {
    /// @notice Structure representing a validator in the factory
    /// @param publicKey The public key of the validator
    /// @param signature The signature used for the deposit
    /// @param feeRecipient The address receiving the exec layer fees
    struct Validator {
        LibPublicKey.PublicKey publicKey;
        LibSignature.Signature signature;
        address feeRecipient;
    }

    /// @notice Structure representing a withdrawal channel in the factory
    /// @param validators The validators in the channel
    /// @param lastEdit The last time the channel was edited (in blocks)
    /// @param limit The staking limit of the channel. Always <= validators.length
    /// @param funded The amount of funded validators in the channel
    struct WithdrawalChannel {
        Validator[] validators;
        uint256 lastEdit;
        uint32 limit;
        uint32 funded;
    }

    /// @notice Structure representing a deposit in the factory
    /// @param index The index of the deposit in the withdrawal channel
    /// @param withdrawalChannel The withdrawal channel of the validator
    /// @param owner The owner of the deposited validator
    struct Deposit {
        uint256 index;
        bytes32 withdrawalChannel;
        address owner;
    }

    /// @notice Structure representing the operator metadata in the factory
    /// @param name The name of the operator
    /// @param url The url of the operator
    /// @param iconUrl The icon url of the operator
    struct Metadata {
        string name;
        string url;
        string iconUrl;
    }

    /// @notice Structure representing the global consensus layer spec held in the global consensus layer spec holder
    /// @param genesisTimestamp The timestamp of the genesis of the consensus layer (slot 0 timestamp)
    /// @param epochsUntilFinal The number of epochs until a block is considered final by the vsuite
    /// @param slotsPerEpoch The number of slots per epoch (32 on mainnet)
    /// @param secondsPerSlot The number of seconds per slot (12 on mainnet)
    struct ConsensusLayerSpec {
        uint64 genesisTimestamp;
        uint64 epochsUntilFinal;
        uint64 slotsPerEpoch;
        uint64 secondsPerSlot;
    }

    /// @notice Structure representing the report bounds held in the pools
    /// @param maxAPRUpperBound The maximum APR upper bound, representing the maximum increase in underlying balance checked at each oracle report
    /// @param maxAPRUpperCoverageBoost The maximum APR upper coverage boost, representing the additional increase allowed when pulling coverage funds
    /// @param maxRelativeLowerBound The maximum relative lower bound, representing the maximum decrease in underlying balance checked at each oracle report
    struct ReportBounds {
        uint64 maxAPRUpperBound;
        uint64 maxAPRUpperCoverageBoost;
        uint64 maxRelativeLowerBound;
    }

    /// @notice Structure representing the consensus layer report submitted by oracle members
    /// @param balanceSum sum of all the balances of all validators that have been activated by the vPool
    ///        this means that as long as the validator was activated, no matter its current status, its balance is taken
    ///        into account
    /// @param exitedSum sum of all the ether that has been exited by the validators that have been activated by the vPool
    ///        to compute this value, we look for withdrawal events inside the block bodies that have happened at an epoch
    ///        that is greater or equal to the withdrawable epoch of a validator purchased by the pool
    ///        when we detect any, we take min(amount,32 eth) into account as exited balance
    /// @param skimmedSum sum of all the ether that has been skimmed by the validators that have been activated by the vPool
    ///        similar to the exitedSum, we look for withdrawal events. If the epochs is lower than the withdrawable epoch
    ///        we take into account the full withdrawal amount, otherwise we take amount - min(amount, 32 eth) into account
    /// @param slashedSum sum of all the ether that has been slashed by the validators that have been activated by the vPool
    ///        to compute this value, we look for validators that are of have been in the slashed state
    ///        then we take the balance of the validator at the epoch prior to its slashing event
    ///        we then add the delta between this old balance and the current balance (or balance just before withdrawal)
    /// @param exiting amount of currently exiting eth, that will soon hit the withdrawal recipient
    ///        this value is computed by taking the balance of any validator in the exit or slashed state or after
    /// @param maxExitable maximum amount that can get requested for exits during report processing
    ///        this value is determined by the oracle. its calculation logic can be updated but all members need to agree and reach
    ///        consensus on the new calculation logic. Its role is to control the rate at which exit requests are performed
    /// @param maxCommittable maximum amount that can get committed for deposits during report processing
    ///        positive value means commit happens before possible exit boosts, negative after
    ///        similar to the mexExitable, this value is determined by the oracle. its calculation logic can be updated but all
    ///        members need to agree and reach consensus on the new calculation logic. Its role is to control the rate at which
    ///        deposit are made. Committed funds are funds that are always a multiple of 32 eth and that cannot be used for
    ///        anything else than purchasing validator, as opposed to the deposited funds that can still be used to fuel the
    ///        exit queue in some cases.
    ///  @param epoch epoch at which the report was crafter
    ///  @param activatedCount current count of validators that have been activated by the vPool
    ///         no matter the current state of the validator, if it has been activated, it has to be accounted inside this value
    ///  @param stoppedCount current count of validators that have been stopped (being in the exit queue, exited or slashed)
    struct ValidatorsReport {
        uint128 balanceSum;
        uint128 exitedSum;
        uint128 skimmedSum;
        uint128 slashedSum;
        uint128 exiting;
        uint128 maxExitable;
        int256 maxCommittable;
        uint64 epoch;
        uint32 activatedCount;
        uint32 stoppedCount;
    }

    /// @notice Structure representing the ethers held in the pools
    /// @param deposited The amount of deposited ethers, that can either be used to boost exits or get committed
    /// @param committed The amount of committed ethers, that can only be used to purchase validators
    struct Ethers {
        uint128 deposited;
        uint128 committed;
    }

    /// @notice Structure representing a ticket in the exit queue
    /// @param position The position of the ticket in the exit queue (equal to the position + size of the previous ticket)
    /// @param size The size of the ticket in the exit queue (in pool shares)
    /// @param maxExitable The maximum amount of ethers that can be exited by the ticket owner (no more rewards in the exit queue, losses are still mutualized)
    struct Ticket {
        uint128 position;
        uint128 size;
        uint128 maxExitable;
    }

    /// @notice Structure representing a cask in the exit queue. This entity is created by the pool upon oracle reports, when exit liquidity is available to feed the exit queue
    /// @param position The position of the cask in the exit queue (equal to the position + size of the previous cask)
    /// @param size The size of the cask in the exit queue (in pool shares)
    /// @param value The value of the cask in the exit queue (in ethers)
    struct Cask {
        uint128 position;
        uint128 size;
        uint128 value;
    }

    type DepositMapping is bytes32;
    type WithdrawalChannelMapping is bytes32;
    type BalanceMapping is bytes32;
    type MetadataStruct is bytes32;
    type ConsensusLayerSpecStruct is bytes32;
    type ReportBoundsStruct is bytes32;
    type ApprovalsMapping is bytes32;
    type ValidatorsReportStruct is bytes32;
    type EthersStruct is bytes32;
    type TicketArray is bytes32;
    type CaskArray is bytes32;
    type FactoryDepositorMapping is bytes32;
}

File 11 of 36 : approvals_mapping.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "./ctypes.sol";

/// @title Approval Mapping Custom Type
library LApprovalsMapping {
    function get(ctypes.ApprovalsMapping position) internal pure returns (mapping(address => mapping(address => uint256)) storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

File 12 of 36 : MultiPool.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/Administrable.sol";
import "utils.sol/types/mapping.sol";
import "utils.sol/types/uint256.sol";
import "utils.sol/types/bool.sol";
import "utils.sol/types/address.sol";

import "vsuite/interfaces/IvPool.sol";
import "./interfaces/IMultiPool.sol";
import "./FeeDispatcher.sol";
import "./ExitQueueClaimHelper.sol";

uint256 constant MIN_COMMISSION_TO_SELL = 1e9; // If there is less than a gwei of commission to sell, we don't sell it

/// @title MultiPool (v1)
/// @author 0xvv @ Kiln
/// @notice This contract contains the common functions to all integration contracts
/// @notice Contains the functions to add pools, activate/deactivate a pool, change the fee of a pool and change the commission distribution
abstract contract MultiPool is IMultiPool, FeeDispatcher, Administrable, ExitQueueClaimHelper {
    using LArray for types.Array;
    using LMapping for types.Mapping;
    using LUint256 for types.Uint256;
    using LBool for types.Bool;

    using CAddress for address;
    using CBool for bool;
    using CUint256 for uint256;

    /// @dev The mapping of pool addresses
    /// @dev Type: mapping(uint256 => address)
    /// @dev Slot: keccak256(bytes("multiPool.1.poolMap")) - 1
    types.Mapping internal constant $poolMap = types.Mapping.wrap(0xbbbff6eb43d00812703825948233d51219dc930ada33999d17cf576c509bebe5);

    /// @dev The mapping of fee amounts in basis point to be applied on rewards from different pools
    /// @dev Type: mapping(uint256 => uint256)
    /// @dev Slot: keccak256(bytes("multiPool.1.fees")) - 1
    types.Mapping internal constant $fees = types.Mapping.wrap(0x725bc5812d869f51ca713008babaeead3e54db7feab7d4cb185136396950f0e3);

    /// @dev The mapping of commission paid for different pools
    /// @dev Type: mapping(uint256 => uint256)
    /// @dev Slot: keccak256(bytes("multiPool.1.commissionPaid")) - 1
    types.Mapping internal constant $commissionPaid = types.Mapping.wrap(0x6c8f9259db4f6802ea7a1e0a01ddb54668b622f1e8d6b610ad7ba4d95f59da29);

    /// @dev The mapping of injected Eth for different pools
    /// @dev Type: mapping(uint256 => uint256)
    /// @dev Slot: keccak256(bytes("multiPool.1.injectedEth")) - 1
    types.Mapping internal constant $injectedEth = types.Mapping.wrap(0x03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a);

    /// @dev The mapping of exited Eth for different pools
    /// @dev Type: mapping(uint256 => uint256)
    /// @dev Slot: keccak256(bytes("multiPool.1.exitedEth")) - 1
    types.Mapping internal constant $exitedEth = types.Mapping.wrap(0x76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b);

    /// @dev The mapping storing whether users can deposit or not to each pool
    /// @dev Type: mapping(uint256 => bool)
    /// @dev Slot: keccak256(bytes("multiPool.1.poolActivation")) - 1
    types.Mapping internal constant $poolActivation = types.Mapping.wrap(0x17b1774c0811229612ec3762023ccd209d6a131e52cdd22f3427eaa8005bcb2f);

    /// @dev The mapping of pool shares owned for each pools
    /// @dev Type: mapping(uint256 => uint256)
    /// @dev Slot: keccak256(bytes("multiPool.1.poolShares")) - 1
    types.Mapping internal constant $poolShares = types.Mapping.wrap(0x357e26a850dc4edaa8b82b6511eec141075372c9c551d3ddb37c35a301f00018);

    /// @dev The number of pools.
    /// @dev Slot: keccak256(bytes("multiPool.1.poolCount")) - 1
    types.Uint256 internal constant $poolCount = types.Uint256.wrap(0xce6dbdcc28927f6ed428550e539c70c9145bd20fc6e3d7611bd20e170e9b1840);

    /// @dev True if deposits are paused
    /// @dev Slot: keccak256(bytes("multiPool.1.depositsPaused")) - 1
    types.Bool internal constant $depositPaused = types.Bool.wrap(0xa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e7);

    /// @dev The maximum commission that can be set for a pool, in basis points, to be set at initialization
    /// @dev Slot: keccak256(bytes("multiPool.1.maxCommission")) - 1
    types.Uint256 internal constant $maxCommission = types.Uint256.wrap(0x70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca8785);

    /// @notice This modifier reverts if the deposit is paused
    modifier notPaused() {
        if ($depositPaused.get()) {
            revert DepositsPaused();
        }
        _;
    }

    /// @inheritdoc IMultiPool
    function pools() public view returns (address[] memory) {
        uint256 length = $poolCount.get();
        address[] memory poolAddresses = new address[](length);
        for (uint256 i = 0; i < length;) {
            poolAddresses[i] = $poolMap.get()[i].toAddress();
            unchecked {
                i++;
            }
        }
        return poolAddresses;
    }

    /// @inheritdoc IMultiPool
    function pauseDeposits(bool isPaused) external onlyAdmin {
        emit SetDepositsPaused(isPaused);
        $depositPaused.set(isPaused);
    }

    /// @inheritdoc IMultiPool
    function depositsPaused() external view returns (bool) {
        return $depositPaused.get();
    }

    /// @inheritdoc IMultiPool
    function getFee(uint256 poolId) public view returns (uint256) {
        return $fees.get()[poolId];
    }

    /// @inheritdoc IMultiPool
    // slither-disable-next-line reentrancy-events
    function changeFee(uint256 poolId, uint256 newFeeBps) external onlyAdmin {
        uint256 earnedBeforeFeeUpdate = _integratorCommissionEarned(poolId);
        _setFee(newFeeBps, poolId);
        uint256 earnedAfterFeeUpdate = _integratorCommissionEarned(poolId);

        uint256 paid = $commissionPaid.get()[poolId];
        uint256 paidAndEarnedAfter = paid + earnedAfterFeeUpdate;
        if (paidAndEarnedAfter < earnedBeforeFeeUpdate) {
            revert CommissionPaidUnderflow();
        }
        $commissionPaid.get()[poolId] = paidAndEarnedAfter - earnedBeforeFeeUpdate;
    }

    /// @inheritdoc IMultiPool
    function changeSplit(address[] calldata recipients, uint256[] calldata splits) external onlyAdmin {
        _setFeeSplit(recipients, splits);
    }

    /// @inheritdoc IMultiPool
    function addPool(address pool, uint256 feeBps) external onlyAdmin {
        _addPool(pool, feeBps);
    }

    /// @inheritdoc IMultiPool
    function getPoolActivation(uint256 poolId) external view returns (bool) {
        return $poolActivation.get()[poolId].toBool();
    }

    /// @inheritdoc IMultiPool
    function integratorCommissionOwed(uint256 poolId) external view returns (uint256) {
        return _integratorCommissionOwed(poolId);
    }

    /// @inheritdoc IMultiPool
    function exitCommissionShares(uint256 poolId) external onlyAdmin {
        _exitCommissionShares(poolId);
    }

    /// @inheritdoc IvPoolSharesReceiver
    function onvPoolSharesReceived(address operator, address from, uint256 amount, bytes memory) external returns (bytes4) {
        uint256 poolId = _findPoolIdOrRevert(msg.sender);
        if (!$poolActivation.get()[poolId].toBool()) revert PoolDisabled(poolId);
        // Check this callback is from minting, we can only receive shares from the pool when depositing
        if ($poolMap.get()[poolId].toAddress() != operator || from != address(0)) {
            revert CallbackNotFromMinting();
        }
        $poolShares.get()[poolId] += amount;
        emit VPoolSharesReceived(msg.sender, poolId, amount);
        return IvPoolSharesReceiver.onvPoolSharesReceived.selector;
    }

    /// PRIVATE METHODS

    /// @dev Internal utility to exit commission shares
    /// @param poolId The vPool id
    // slither-disable-next-line reentrancy-events
    function _exitCommissionShares(uint256 poolId) internal {
        if (poolId >= $poolCount.get()) revert InvalidPoolId(poolId);
        uint256 shares = _poolSharesOfIntegrator(poolId);
        if (shares == 0) revert NoSharesToExit(poolId);
        address[] memory recipients = $feeRecipients.toAddressA();
        uint256[] memory weights = $feeSplits.toUintA();
        IvPool pool = _getPool(poolId);
        for (uint256 i = 0; i < recipients.length;) {
            uint256 share = LibUint256.mulDiv(shares, weights[i], LibConstant.BASIS_POINTS_MAX);
            if (share > 0) {
                _sendSharesToExitQueue(poolId, share, pool, recipients[i]);
            }
            unchecked {
                ++i;
            }
        }
        $exitedEth.get()[poolId] += LibUint256.mulDiv(shares, pool.totalUnderlyingSupply(), pool.totalSupply());
        $commissionPaid.get()[poolId] = _integratorCommissionEarned(poolId);
        emit ExitedCommissionShares(poolId, shares, weights, recipients);
    }

    /// @dev Internal utility to send pool shares to the exit queue
    // slither-disable-next-line calls-loop
    function _sendSharesToExitQueue(uint256 poolId, uint256 shares, IvPool pool, address ticketOwner) internal {
        $poolShares.get()[poolId] -= shares;
        bool result = pool.transferShares(pool.exitQueue(), shares, abi.encodePacked(ticketOwner));
        if (!result) {
            revert PoolTransferFailed(poolId);
        }
    }

    /// @notice Internal utility to find the id of a pool using its address
    /// @dev Reverts if the address is not found
    /// @param poolAddress address of the pool to look up
    function _findPoolIdOrRevert(address poolAddress) internal view returns (uint256) {
        for (uint256 id = 0; id < $poolCount.get();) {
            if (poolAddress == $poolMap.get()[id].toAddress()) {
                return id;
            }
            unchecked {
                id++;
            }
        }
        revert NotARegisteredPool(poolAddress);
    }

    /// @dev Internal utility to set the integrator fee value
    /// @param integratorFeeBps The new integrator fee in bps
    /// @param poolId The vPool id
    function _setFee(uint256 integratorFeeBps, uint256 poolId) internal {
        if (integratorFeeBps > $maxCommission.get()) {
            revert FeeOverMax($maxCommission.get());
        }
        $fees.get()[poolId] = integratorFeeBps;
        emit SetFee(poolId, integratorFeeBps);
    }

    /// @dev Internal utility to get get the pool address
    /// @param poolId The index of the pool
    /// @return The pool
    // slither-disable-next-line naming-convention
    function _getPool(uint256 poolId) public view returns (IvPool) {
        if (poolId >= $poolCount.get()) {
            revert InvalidPoolId(poolId);
        }
        return IvPool($poolMap.get()[poolId].toAddress());
    }

    /// @dev Add a pool to the list.
    /// @param newPool new pool address.
    /// @param fee fees in basis points of ETH.
    // slither-disable-next-line dead-code
    function _addPool(address newPool, uint256 fee) internal {
        LibSanitize.notInvalidBps(fee);
        LibSanitize.notZeroAddress(newPool);
        uint256 poolId = $poolCount.get();
        for (uint256 i = 0; i < poolId;) {
            if (newPool == $poolMap.get()[i].toAddress()) {
                revert PoolAlreadyRegistered(newPool);
            }
            unchecked {
                i++;
            }
        }

        $poolMap.get()[poolId] = newPool.v();
        $fees.get()[poolId] = fee;
        $poolActivation.get()[poolId] = true.v();
        $poolCount.set(poolId + 1);

        emit PoolAdded(newPool, poolId);
        emit SetFee(poolId, fee);
    }

    /// @dev Reverts if the given pool is not enabled.
    /// @param poolId pool id.
    // slither-disable-next-line dead-code
    function _checkPoolIsEnabled(uint256 poolId) internal view {
        if (poolId >= $poolCount.get()) {
            revert InvalidPoolId(poolId);
        }
        bool status = $poolActivation.get()[poolId].toBool();
        if (!status) {
            revert PoolDisabled(poolId);
        }
    }

    /// @dev Returns the ETH value of the vPool shares in the contract.
    /// @return amount of ETH.
    // slither-disable-next-line calls-loop
    function _stakedEthValue(uint256 poolId) internal view returns (uint256) {
        IvPool pool = _getPool(poolId);
        uint256 poolTotalSupply = pool.totalSupply();
        if (poolTotalSupply == 0) {
            return 0;
        }
        return LibUint256.mulDiv($poolShares.get()[poolId], pool.totalUnderlyingSupply(), poolTotalSupply);
    }

    /// @dev Returns the amount of ETH earned by the integrator.
    /// @return amount of ETH.
    function _integratorCommissionEarned(uint256 poolId) internal view returns (uint256) {
        uint256 staked = _stakedEthValue(poolId);
        uint256 injected = $injectedEth.get()[poolId];
        uint256 exited = $exitedEth.get()[poolId];
        if (injected >= staked + exited) {
            // Can happen right after staking due to rounding error
            return 0;
        }
        uint256 rewardsEarned = staked + exited - injected;
        return LibUint256.mulDiv(rewardsEarned, $fees.get()[poolId], LibConstant.BASIS_POINTS_MAX);
    }

    /// @dev Returns the amount of ETH owed to the integrator.
    /// @return amount of ETH.
    // slither-disable-next-line dead-code
    function _integratorCommissionOwed(uint256 poolId) internal view returns (uint256) {
        uint256 earned = _integratorCommissionEarned(poolId);
        uint256 paid = $commissionPaid.get()[poolId];
        if (earned > paid) {
            return earned - paid;
        } else {
            return 0;
        }
    }

    /// @dev Returns the ETH value of the vPool shares after subtracting integrator commission.
    /// @return amount of ETH.
    // slither-disable-next-line dead-code
    function _ethAfterCommission(uint256 poolId) internal view returns (uint256) {
        return _stakedEthValue(poolId) - _integratorCommissionOwed(poolId);
    }

    /// @dev Returns the number of vPool shares owed as commission.
    /// @return amount of shares.
    // slither-disable-next-line calls-loop,dead-code
    function _poolSharesOfIntegrator(uint256 poolId) internal view returns (uint256) {
        IvPool pool = IvPool($poolMap.get()[poolId].toAddress());
        uint256 poolTotalUnderlying = pool.totalUnderlyingSupply();
        return poolTotalUnderlying == 0 ? 0 : LibUint256.mulDiv(_integratorCommissionOwed(poolId), pool.totalSupply(), poolTotalUnderlying);
    }

    /// @dev Internal utility to set the max commission value
    /// @param maxCommission The new max commission in bps
    // slither-disable-next-line dead-code
    function _setMaxCommission(uint256 maxCommission) internal {
        LibSanitize.notInvalidBps(maxCommission);
        $maxCommission.set(maxCommission);
        emit SetMaxCommission(maxCommission);
    }
}

File 13 of 36 : IMultiPool20.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

/// @title MultiPool-20 (V1) Interface
/// @author 0xvv @ Kiln
/// @notice This contract contains the internal logic for an ERC-20 token based on one or multiple pools.
interface IMultiPool20 {
    /// @notice Emitted when a stake is transferred.
    /// @param from The address sending the stake
    /// @param to The address receiving the stake
    /// @param value The transfer amount
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Emitted when an allowance is created.
    /// @param owner The owner of the shares
    /// @param spender The address that can spend
    /// @param value The allowance amount
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Emitted when some integrator shares are sold
    /// @param pSharesSold ETH amount of vPool shares sold
    /// @param id Id of the pool
    /// @param amountSold ETH amount of shares sold
    event CommissionSharesSold(uint256 pSharesSold, uint256 id, uint256 amountSold);

    /// @notice Emitted when new split is set.
    /// @param split Array of value in basis points to route to each pool
    event SetPoolPercentages(uint256[] split);

    /// @notice Thrown when a transfer is attempted but the sender does not have enough balance.
    /// @param amount The token amount.
    /// @param balance The balance of user.
    error InsufficientBalance(uint256 amount, uint256 balance);

    /// @notice Thrown when a transferFrom is attempted but the spender does not have enough allowance.
    error InsufficientAllowance(uint256 amount, uint256 allowance);

    /// @notice Thrown when trying to set a pool percentage != 0 to a deactivated pool
    error NonZeroPercentageOnDeactivatedPool(uint256 id);

    /// @notice Set the percentage of new stakes to route to each pool
    /// @notice If a pool is disabled it needs to be set to 0 in the array
    /// @param split Array of values in basis points to route to each pool
    function setPoolPercentages(uint256[] calldata split) external;

    /// @notice Burns the sender's shares and sends the exitQueue tickets to the caller.
    /// @param amount Amount of shares to add to the exit queue
    function requestExit(uint256 amount) external;

    /// @notice Returns the share to ETH conversion rate
    /// @return ETH value of a share
    function rate() external returns (uint256);

    /// @notice Allows the integrator to prevent users from depositing to a vPool.
    /// @param poolId The id of the vPool.
    /// @param status Whether the users can deposit to the pool.
    /// @param newPoolPercentages Array of value in basis points to route to each pool after the change
    function setPoolActivation(uint256 poolId, bool status, uint256[] calldata newPoolPercentages) external;
}

File 14 of 36 : victypes.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import "utils.sol/libs/LibPublicKey.sol";
import "utils.sol/libs/LibSignature.sol";

/// @title Custom Types
// slither-disable-next-line naming-convention
library victypes {
    struct User4907 {
        address user;
        uint64 expiration;
    }

    type BalanceMapping is bytes32;
    type User4907Mapping is bytes32;
}

File 15 of 36 : balance.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import "./victypes.sol";

/// @title Balance mappings Custom Type
library LBalance {
    // slither-disable-next-line dead-code
    function get(victypes.BalanceMapping position) internal pure returns (mapping(address => uint256) storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

File 16 of 36 : LibPublicKey.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

library LibPublicKey {
    // slither-disable-next-line unused-state
    uint256 constant PUBLIC_KEY_LENGTH = 48;

    // slither-disable-next-line unused-state
    bytes constant PADDING = hex"00000000000000000000000000000000";

    struct PublicKey {
        bytes32 A;
        bytes16 B;
    }

    // slither-disable-next-line dead-code
    function toBytes(PublicKey memory publicKey) internal pure returns (bytes memory) {
        return abi.encodePacked(publicKey.A, publicKey.B);
    }

    // slither-disable-next-line dead-code
    function fromBytes(bytes memory publicKey) internal pure returns (PublicKey memory ret) {
        publicKey = bytes.concat(publicKey, PADDING);
        (bytes32 A, bytes32 B_prime) = abi.decode(publicKey, (bytes32, bytes32));
        bytes16 B = bytes16(uint128(uint256(B_prime) >> 128));
        ret.A = A;
        ret.B = B;
    }
}

File 17 of 36 : LibSignature.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

library LibSignature {
    // slither-disable-next-line unused-state
    uint256 constant SIGNATURE_LENGTH = 96;

    struct Signature {
        bytes32 A;
        bytes32 B;
        bytes32 C;
    }

    // slither-disable-next-line dead-code
    function toBytes(Signature memory signature) internal pure returns (bytes memory) {
        return abi.encodePacked(signature.A, signature.B, signature.C);
    }

    // slither-disable-next-line dead-code
    function fromBytes(bytes memory signature) internal pure returns (Signature memory ret) {
        (ret) = abi.decode(signature, (Signature));
    }
}

File 18 of 36 : Administrable.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./libs/LibSanitize.sol";
import "./types/address.sol";
import "./interfaces/IAdministrable.sol";

/// @title Administrable
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice This contract provides all the utilities to handle the administration and its transfer.
abstract contract Administrable is IAdministrable {
    using LAddress for types.Address;

    /// @dev The admin address in storage.
    /// @dev Slot: keccak256(bytes("administrable.admin")) - 1
    types.Address internal constant $admin =
        types.Address.wrap(0x927a17e5ea75d9461748062a2652f4d3698a628896c9832f8488fa0d2846af09);
    /// @dev The pending admin address in storage.
    /// @dev Slot: keccak256(bytes("administrable.pendingAdmin")) - 1
    types.Address internal constant $pendingAdmin =
        types.Address.wrap(0x3c1eebcc225c6cc7f5f8765767af6eff617b4139dc3624923a2db67dbca7b68e);

    /// @dev This modifier ensures that only the admin is able to call the method.
    modifier onlyAdmin() {
        if (msg.sender != _getAdmin()) {
            revert LibErrors.Unauthorized(msg.sender, _getAdmin());
        }
        _;
    }

    /// @dev This modifier ensures that only the pending admin is able to call the method.
    modifier onlyPendingAdmin() {
        if (msg.sender != _getPendingAdmin()) {
            revert LibErrors.Unauthorized(msg.sender, _getPendingAdmin());
        }
        _;
    }

    /// @inheritdoc IAdministrable
    function admin() external view returns (address) {
        return _getAdmin();
    }

    /// @inheritdoc IAdministrable
    function pendingAdmin() external view returns (address) {
        return _getPendingAdmin();
    }

    /// @notice Propose a new admin.
    /// @dev Only callable by the admin.
    /// @param newAdmin The new admin to propose
    function transferAdmin(address newAdmin) external onlyAdmin {
        _setPendingAdmin(newAdmin);
    }

    /// @notice Accept an admin transfer.
    /// @dev Only callable by the pending admin.
    function acceptAdmin() external onlyPendingAdmin {
        _setAdmin(msg.sender);
        _setPendingAdmin(address(0));
    }

    /// @dev Retrieve the admin address.
    /// @return The admin address
    function _getAdmin() internal view returns (address) {
        return $admin.get();
    }

    /// @dev Change the admin address.
    /// @param newAdmin The new admin address
    function _setAdmin(address newAdmin) internal {
        LibSanitize.notZeroAddress(newAdmin);
        emit SetAdmin(newAdmin);
        $admin.set(newAdmin);
    }

    /// @dev Retrieve the pending admin address.
    /// @return The pending admin address
    function _getPendingAdmin() internal view returns (address) {
        return $pendingAdmin.get();
    }

    /// @dev Change the pending admin address.
    /// @param newPendingAdmin The new pending admin address
    function _setPendingAdmin(address newPendingAdmin) internal {
        emit SetPendingAdmin(newPendingAdmin);
        $pendingAdmin.set(newPendingAdmin);
    }
}

File 19 of 36 : bool.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

library LBool {
    // slither-disable-next-line dead-code
    function get(types.Bool position) internal view returns (bool data) {
        // slither-disable-next-line assembly
        assembly {
            data := sload(position)
        }
    }

    // slither-disable-next-line dead-code
    function set(types.Bool position, bool data) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, data)
        }
    }

    // slither-disable-next-line dead-code
    function del(types.Bool position) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, 0)
        }
    }
}

library CBool {
    // slither-disable-next-line dead-code
    function toBytes32(bool val) internal pure returns (bytes32) {
        return bytes32(toUint256(val));
    }

    // slither-disable-next-line dead-code
    function toAddress(bool val) internal pure returns (address) {
        return address(uint160(toUint256(val)));
    }

    // slither-disable-next-line dead-code
    function toUint256(bool val) internal pure returns (uint256 converted) {
        // slither-disable-next-line assembly
        assembly {
            converted := iszero(iszero(val))
        }
    }

    /// @dev This method should be used to convert a bool to a uint256 when used as a key in a mapping.
    // slither-disable-next-line dead-code
    function k(bool val) internal pure returns (uint256) {
        return toUint256(val);
    }

    /// @dev This method should be used to convert a bool to a uint256 when used as a value in a mapping.
    // slither-disable-next-line dead-code
    function v(bool val) internal pure returns (uint256) {
        return toUint256(val);
    }
}

File 20 of 36 : address.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

/// @notice Library Address - Address slot utilities.
library LAddress {
    // slither-disable-next-line dead-code, assembly
    function get(types.Address position) internal view returns (address data) {
        // slither-disable-next-line assembly
        assembly {
            data := sload(position)
        }
    }

    // slither-disable-next-line dead-code
    function set(types.Address position, address data) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, data)
        }
    }

    // slither-disable-next-line dead-code
    function del(types.Address position) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, 0)
        }
    }
}

library CAddress {
    // slither-disable-next-line dead-code
    function toUint256(address val) internal pure returns (uint256) {
        return uint256(uint160(val));
    }

    // slither-disable-next-line dead-code
    function toBytes32(address val) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(val)));
    }

    // slither-disable-next-line dead-code
    function toBool(address val) internal pure returns (bool converted) {
        // slither-disable-next-line assembly
        assembly {
            converted := gt(val, 0)
        }
    }

    /// @notice This method should be used to convert an address to a uint256 when used as a key in a mapping.
    // slither-disable-next-line dead-code
    function k(address val) internal pure returns (uint256) {
        return toUint256(val);
    }

    /// @notice This method should be used to convert an address to a uint256 when used as a value in a mapping.
    // slither-disable-next-line dead-code
    function v(address val) internal pure returns (uint256) {
        return toUint256(val);
    }
}

File 21 of 36 : IvPool.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/interfaces/IFixable.sol";

import "../ctypes/ctypes.sol";

/// @title Pool Interface
/// @author mortimr @ Kiln
/// @notice The vPool contract is in charge of pool funds and fund validators from the vFactory
interface IvPool is IFixable {
    /// @notice Emitted at construction time when all contract addresses are set
    /// @param factory The address of the vFactory contract
    /// @param withdrawalRecipient The address of the withdrawal recipient contract
    /// @param execLayerRecipient The address of the execution layer recipient contract
    /// @param coverageRecipient The address of the coverage recipient contract
    /// @param oracleAggregator The address of the oracle aggregator contract
    /// @param exitQueue The address of the exit queue contract
    event SetContractLinks(
        address factory,
        address withdrawalRecipient,
        address execLayerRecipient,
        address coverageRecipient,
        address oracleAggregator,
        address exitQueue
    );

    /// @notice Emitted when the global validator extra data is changed
    /// @param extraData New extra data used on validator purchase
    event SetValidatorGlobalExtraData(string extraData);

    /// @notice Emitted when a depositor authorization changed
    /// @param depositor The address of the depositor
    /// @param allowed True if allowed to deposit
    event ApproveDepositor(address depositor, bool allowed);

    /// @notice Emitted when a depositor performs a deposit
    /// @param sender The transaction sender
    /// @param amount The deposit amount
    /// @param mintedShares The amount of shares created
    event Deposit(address indexed sender, uint256 amount, uint256 mintedShares);

    /// @notice Emitted when the vPool purchases validators to the vFactory
    /// @param validators The list of IDs (not BLS Public keys)
    event PurchasedValidators(uint256[] validators);

    /// @notice Emitted when new shares are created
    /// @param account The account receiving the new shares
    /// @param amount The amount of shares created
    /// @param totalSupply The new totalSupply value
    event Mint(address indexed account, uint256 amount, uint256 totalSupply);

    /// @notice Emitted when shares are burned
    /// @param burner The account burning shares
    /// @param amount The amount of burned shares
    /// @param totalSupply The new totalSupply value
    event Burn(address burner, uint256 amount, uint256 totalSupply);

    /// @notice Emitted when shares are transfered
    /// @param from The account sending the shares
    /// @param to The account receiving the shares
    /// @param value The value transfered
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Emitted when shares are approved for a spender
    /// @param owner The account approving the shares
    /// @param spender The account receiving the spending rights
    /// @param value The value of the approval. Max uint256 means infinite (will never decrease)
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Emitted when shares are voided (action of burning without redeeming anything on purpose)
    /// @param voider The account voiding the shares
    /// @param amount The amount of voided shares
    event VoidedShares(address voider, uint256 amount);

    /// @notice Emitted when ether is injected into the system (outside of the deposit flow)
    /// @param injecter The account injecting the ETH
    /// @param amount The amount of injected ETH
    event InjectedEther(address injecter, uint256 amount);

    /// @notice Emitted when the report processing is finished
    /// @param epoch The epoch number
    /// @param report The received report structure
    /// @param traces Internal traces with key figures
    event ProcessedReport(uint256 indexed epoch, ctypes.ValidatorsReport report, ReportTraces traces);

    /// @notice Emitted when rewards are distributed to the node operator
    /// @param operatorTreasury The address receiving the rewards
    /// @param sharesCount The amount of shares created to pay the rewards
    /// @param sharesValue The value in ETH of the newly minted shares
    /// @param totalSupply The updated totalSupply value
    /// @param totalUnderlyingSupply The updated totalUnderlyingSupply value
    event DistributedOperatorRewards(
        address indexed operatorTreasury, uint256 sharesCount, uint256 sharesValue, uint256 totalSupply, uint256 totalUnderlyingSupply
    );

    /// @notice Emitted when the report bounds are updated
    /// @param maxAPRUpperBound The maximum APR allowed during oracle reports
    /// @param maxAPRUpperCoverageBoost The APR boost allowed only for coverage funds
    /// @param maxRelativeLowerBound The max relative delta in underlying supply authorized during losses of funds
    event SetReportBounds(uint64 maxAPRUpperBound, uint64 maxAPRUpperCoverageBoost, uint64 maxRelativeLowerBound);

    /// @notice Emitted when the epochs per frame value is updated
    /// @param epochsPerFrame The new epochs per frame value
    event SetEpochsPerFrame(uint256 epochsPerFrame);

    /// @notice Emitted when the consensus layer spec is updated
    /// @param consensusLayerSpec The new consensus layer spec
    event SetConsensusLayerSpec(ctypes.ConsensusLayerSpec consensusLayerSpec);

    /// @notice Emitted when the operator fee is updated
    /// @param operatorFeeBps The new operator fee value
    event SetOperatorFee(uint256 operatorFeeBps);

    /// @notice Emitted when the deposited ether buffer is updated
    /// @param depositedEthers The new deposited ethers value
    event SetDepositedEthers(uint256 depositedEthers);

    /// @notice Emitted when the committed ether buffer is updated
    /// @param committedEthers The new committed ethers value
    event SetCommittedEthers(uint256 committedEthers);

    /// @notice Emitted when the requested exits is updated
    /// @param newRequestedExits The new requested exits count
    event SetRequestedExits(uint32 newRequestedExits);

    /// @notice The balance was too low for the requested operation
    /// @param account The account trying to perform the operation
    /// @param currentBalance The current account balance
    /// @param requiredAmount The amount that was required to perform the operation
    error BalanceTooLow(address account, uint256 currentBalance, uint256 requiredAmount);

    /// @notice The allowance was too low for the requested operation
    /// @param account The account trying to perform the operation
    /// @param operator The account triggering the operation on behalf of the account
    /// @param currentApproval The current account approval towards the operator
    /// @param requiredAmount The amount that was required to perform the operation
    error AllowanceTooLow(address account, address operator, uint256 currentApproval, uint256 requiredAmount);

    /// @notice Thrown when approval for an account and spender is already zero.
    /// @param account The account for which approval was attempted to be set to zero.
    /// @param spender The spender for which approval was attempted to be set to zero.
    error ApprovalAlreadyZero(address account, address spender);

    /// @notice Thrown when there is an error with a share receiver.
    /// @param err The error message.
    error ShareReceiverError(string err);

    /// @notice Thrown when there is no validator available to purchase.
    error NoValidatorToPurchase();

    /// @notice Thrown when the epoch of a report is too old.
    /// @param epoch The epoch of the report.
    /// @param expectEpoch The expected epoch for the operation.
    error EpochTooOld(uint256 epoch, uint256 expectEpoch);

    /// @notice Thrown when an epoch is not the first epoch of a frame.
    /// @param epoch The epoch that was not the first epoch of a frame.
    error EpochNotFrameFirst(uint256 epoch);

    /// @notice Thrown when an epoch is not final.
    /// @param epoch The epoch that was not final.
    /// @param currentTimestamp The current timestamp.
    /// @param finalTimestamp The final timestamp of the frame.
    error EpochNotFinal(uint256 epoch, uint256 currentTimestamp, uint256 finalTimestamp);

    /// @notice Thrown when the validator count is decreasing.
    /// @param previousValidatorCount The previous validator count.
    /// @param validatorCount The current validator count.
    error DecreasingValidatorCount(uint256 previousValidatorCount, uint256 validatorCount);

    /// @notice Thrown when the stopped validator count is decreasing.
    /// @param previousStoppedValidatorCount The previous stopped validator count.
    /// @param stoppedValidatorCount The current stopped validator count.
    error DecreasingStoppedValidatorCount(uint256 previousStoppedValidatorCount, uint256 stoppedValidatorCount);

    /// @notice Thrown when the slashed balance sum is decreasing.
    /// @param reportedSlashedBalanceSum The reported slashed balance sum.
    /// @param lastReportedSlashedBalanceSum The last reported slashed balance sum.
    error DecreasingSlashedBalanceSum(uint256 reportedSlashedBalanceSum, uint256 lastReportedSlashedBalanceSum);

    /// @notice Thrown when the exited balance sum is decreasing.
    /// @param reportedExitedBalanceSum The reported exited balance sum.
    /// @param lastReportedExitedBalanceSum The last reported exited balance sum.
    error DecreasingExitedBalanceSum(uint256 reportedExitedBalanceSum, uint256 lastReportedExitedBalanceSum);

    /// @notice Thrown when the skimmed balance sum is decreasing.
    /// @param reportedSkimmedBalanceSum The reported skimmed balance sum.
    /// @param lastReportedSkimmedBalanceSum The last reported skimmed balance sum.
    error DecreasingSkimmedBalanceSum(uint256 reportedSkimmedBalanceSum, uint256 lastReportedSkimmedBalanceSum);

    /// @notice Thrown when the reported validator count is higher than the total activated validators
    /// @param stoppedValidatorsCount The reported stopped validator count.
    /// @param maxStoppedValidatorsCount The maximum allowed stopped validator count.
    error StoppedValidatorCountTooHigh(uint256 stoppedValidatorsCount, uint256 maxStoppedValidatorsCount);

    /// @notice Thrown when the reported exiting balance exceeds the total validator balance on the cl
    /// @param exiting The reported exiting balance.
    /// @param balance The total validator balance on the cl.
    error ExitingBalanceTooHigh(uint256 exiting, uint256 balance);

    /// @notice Thrown when the reported validator count is higher than the deposited validator count.
    /// @param reportedValidatorCount The reported validator count.
    /// @param depositedValidatorCount The deposited validator count.
    error ValidatorCountTooHigh(uint256 reportedValidatorCount, uint256 depositedValidatorCount);

    /// @notice Thrown when the coverage is higher than the loss.
    /// @param coverage The coverage.
    /// @param loss The loss.
    error CoverageHigherThanLoss(uint256 coverage, uint256 loss);

    /// @notice Thrown when the balance increase exceeds the maximum allowed balance increase.
    /// @param balanceIncrease The balance increase.
    /// @param maximumAllowedBalanceIncrease The maximum allowed balance increase.
    error UpperBoundCrossed(uint256 balanceIncrease, uint256 maximumAllowedBalanceIncrease);

    /// @notice Thrown when the balance increase exceeds the maximum allowed balance increase or maximum allowed coverage.
    /// @param balanceIncrease The balance increase.
    /// @param maximumAllowedBalanceIncrease The maximum allowed balance increase.
    /// @param maximumAllowedCoverage The maximum allowed coverage.
    error BoostedBoundCrossed(uint256 balanceIncrease, uint256 maximumAllowedBalanceIncrease, uint256 maximumAllowedCoverage);

    /// @notice Thrown when the balance decrease exceeds the maximum allowed balance decrease.
    /// @param balanceDecrease The balance decrease.
    /// @param maximumAllowedBalanceDecrease The maximum allowed balance decrease.
    error LowerBoundCrossed(uint256 balanceDecrease, uint256 maximumAllowedBalanceDecrease);

    /// @notice Thrown when the amount of shares to mint is computed to 0
    error InvalidNullMint();

    /// @notice Traces emitted at the end of the reporting process.
    /// @param preUnderlyingSupply The pre-reporting underlying supply.
    /// @param postUnderlyingSupply The post-reporting underlying supply.
    /// @param preSupply The pre-reporting supply.
    /// @param postSupply The post-reporting supply.
    /// @param newExitedEthers The new exited ethers.
    /// @param newSkimmedEthers The new skimmed ethers.
    /// @param exitBoostEthers The exit boost ethers.
    /// @param exitFedEthers The exit fed ethers.
    /// @param exitBurnedShares The exit burned shares.
    /// @param exitingProjection The exiting projection.
    /// @param baseFulfillableDemand The base fulfillable demand.
    /// @param extraFulfillableDemand The extra fulfillable demand.
    /// @param rewards The rewards. Can be negative when there is a loss, but cannot include coverage funds.
    /// @param delta The delta. Can be negative when there is a loss and include all pulled funds.
    /// @param increaseLimit The increase limit.
    /// @param coverageIncreaseLimit The coverage increase limit.
    /// @param decreaseLimit The decrease limit.
    /// @param consensusLayerDelta The consensus layer delta.
    /// @param pulledCoverageFunds The pulled coverage funds.
    /// @param pulledExecutionLayerRewards The pulled execution layer rewards.
    /// @param pulledExitQueueUnclaimedFunds The pulled exit queue unclaimed funds.
    struct ReportTraces {
        // supplied
        uint128 preUnderlyingSupply;
        uint128 postUnderlyingSupply;
        uint128 preSupply;
        uint128 postSupply;
        // new consensus layer funds
        uint128 newExitedEthers;
        uint128 newSkimmedEthers;
        // exit related funds
        uint128 exitBoostEthers;
        uint128 exitFedEthers;
        uint128 exitBurnedShares;
        uint128 exitingProjection;
        uint128 baseFulfillableDemand;
        uint128 extraFulfillableDemand;
        // rewards
        int128 rewards;
        // delta and details about sources of funds
        int128 delta;
        uint128 increaseLimit;
        uint128 coverageIncreaseLimit;
        uint128 decreaseLimit;
        int128 consensusLayerDelta;
        uint128 pulledCoverageFunds;
        uint128 pulledExecutionLayerRewards;
        uint128 pulledExitQueueUnclaimedFunds;
    }

    /// @notice Initializes the contract with the given parameters.
    /// @param addrs The addresses of the dependencies (factory, withdrawal recipient, exec layer recipient,
    ///              coverage recipient, oracle aggregator, exit queue).
    /// @param epochsPerFrame_ The number of epochs per frame.
    /// @param consensusLayerSpec_ The consensus layer spec.
    /// @param bounds_ The bounds for reporting.
    /// @param operatorFeeBps_ The operator fee in basis points.
    /// @param extraData_ The initial extra data that will be provided on each deposit
    function initialize(
        address[6] calldata addrs,
        uint256 epochsPerFrame_,
        ctypes.ConsensusLayerSpec calldata consensusLayerSpec_,
        uint64[3] calldata bounds_,
        uint256 operatorFeeBps_,
        string calldata extraData_
    ) external;

    /// @notice Returns the address of the factory contract.
    /// @return The address of the factory contract.
    function factory() external view returns (address);

    /// @notice Returns the address of the execution layer recipient contract.
    /// @return The address of the execution layer recipient contract.
    function execLayerRecipient() external view returns (address);

    /// @notice Returns the address of the coverage recipient contract.
    /// @return The address of the coverage recipient contract.
    function coverageRecipient() external view returns (address);

    /// @notice Returns the address of the withdrawal recipient contract.
    /// @return The address of the withdrawal recipient contract.
    function withdrawalRecipient() external view returns (address);

    /// @notice Returns the address of the oracle aggregator contract.
    /// @return The address of the oracle aggregator contract.
    function oracleAggregator() external view returns (address);

    /// @notice Returns the address of the exit queue contract
    /// @return The address of the exit queue contract
    function exitQueue() external view returns (address);

    /// @notice Returns the current validator global extra data
    /// @return The validator global extra data value
    function validatorGlobalExtraData() external view returns (string memory);

    /// @notice Returns whether the given address is a depositor.
    /// @param depositorAddress The address to check.
    /// @return Whether the given address is a depositor.
    function depositors(address depositorAddress) external view returns (bool);

    /// @notice Returns the total supply of tokens.
    /// @return The total supply of tokens.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the name of the vPool
    /// @return The name of the vPool
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the vPool
    /// @return The symbol of the vPool
    function symbol() external view returns (string memory);

    /// @notice Returns the decimals of the vPool shares
    /// @return The decimal count
    function decimals() external pure returns (uint8);

    /// @notice Returns the total underlying supply of tokens.
    /// @return The total underlying supply of tokens.
    function totalUnderlyingSupply() external view returns (uint256);

    /// @notice Returns the current ETH/SHARES rate based on the total underlying supply and total supply.
    /// @return The current rate
    function rate() external view returns (uint256);

    /// @notice Returns the current requested exit count
    /// @return The current requested exit count
    function requestedExits() external view returns (uint32);

    /// @notice Returns the balance of the given account.
    /// @param account The address of the account to check.
    /// @return The balance of the given account.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the allowance of the given spender for the given owner.
    /// @param owner The owner of the allowance.
    /// @param spender The spender of the allowance.
    /// @return The allowance of the given spender for the given owner.
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Returns the details about the held ethers
    /// @return The structure of ethers inside the contract
    function ethers() external view returns (ctypes.Ethers memory);

    /// @notice Returns an array of the IDs of purchased validators.
    /// @return An array of the IDs of purchased validators.
    function purchasedValidators() external view returns (uint256[] memory);

    /// @notice Returns the ID of the purchased validator at the given index.
    /// @param idx The index of the validator.
    /// @return The ID of the purchased validator at the given index.
    function purchasedValidatorAtIndex(uint256 idx) external view returns (uint256);

    /// @notice Returns the total number of purchased validators.
    /// @return The total number of purchased validators.
    function purchasedValidatorCount() external view returns (uint256);

    /// @notice Returns the last epoch.
    /// @return The last epoch.
    function lastEpoch() external view returns (uint256);

    /// @notice Returns the last validator report that was processed
    /// @return The last report structure.
    function lastReport() external view returns (ctypes.ValidatorsReport memory);

    /// @notice Returns the total amount in ETH covered by the contract.
    /// @return The total amount in ETH covered by the contract.
    function totalCovered() external view returns (uint256);

    /// @notice Returns the number of epochs per frame.
    /// @return  The number of epochs per frame.
    function epochsPerFrame() external view returns (uint256);

    /// @notice Returns the consensus layer spec.
    /// @return The consensus layer spec.
    function consensusLayerSpec() external pure returns (ctypes.ConsensusLayerSpec memory);

    /// @notice Returns the report bounds.
    /// @return maxAPRUpperBound The maximum APR for the upper bound.
    /// @return maxAPRUpperCoverageBoost The maximum APR for the upper bound with coverage boost.
    /// @return maxRelativeLowerBound The maximum relative lower bound.
    function reportBounds()
        external
        view
        returns (uint64 maxAPRUpperBound, uint64 maxAPRUpperCoverageBoost, uint64 maxRelativeLowerBound);

    /// @notice Returns the operator fee.
    /// @return  The operator fee.
    function operatorFee() external view returns (uint256);

    /// @notice Returns whether the given epoch is valid.
    /// @param epoch The epoch to check.
    /// @return Whether the given epoch is valid.
    function isValidEpoch(uint256 epoch) external view returns (bool);

    /// @notice Reverts if given epoch is invalid, with an explicit custom error based on the issue
    /// @param epoch The epoch to check.
    function onlyValidEpoch(uint256 epoch) external view;

    /// @notice Allows or disallows the given depositor to deposit.
    /// @param depositorAddress The address of the depositor.
    /// @param allowed Whether the depositor is allowed to deposit.
    function allowDepositor(address depositorAddress, bool allowed) external;

    /// @notice Transfers the given amount of shares to the given address.
    /// @param to The address to transfer the shares to.
    /// @param amount The amount of shares to transfer.
    /// @param data Additional data for the transfer.
    /// @return Whether the transfer was successful.
    function transferShares(address to, uint256 amount, bytes calldata data) external returns (bool);

    /// @notice Increases the allowance for the given spender by the given amount.
    /// @param spender The spender to increase the allowance for.
    /// @param amount The amount to increase the allowance by.
    /// @return Whether the increase was successful.
    function increaseAllowance(address spender, uint256 amount) external returns (bool);

    /// @notice Decreases the allowance of a spender by the given amount.
    /// @param spender The address of the spender.
    /// @param amount The amount to decrease the allowance by.
    /// @return Whether the allowance was successfully decreased.
    function decreaseAllowance(address spender, uint256 amount) external returns (bool);

    /// @notice Voids the allowance of a spender.
    /// @param spender The address of the spender.
    /// @return Whether the allowance was successfully voided.
    function voidAllowance(address spender) external returns (bool);

    /// @notice Transfers shares from one account to another.
    /// @param from The address of the account to transfer shares from.
    /// @param to The address of the account to transfer shares to.
    /// @param amount The amount of shares to transfer.
    /// @param data Optional data to include with the transaction.
    /// @return  Whether the transfer was successful.
    function transferSharesFrom(address from, address to, uint256 amount, bytes calldata data) external returns (bool);

    /// @notice Deposits ether into the contract.
    /// @return  The number of shares minted on deposit
    function deposit() external payable returns (uint256);

    /// @notice Purchases the maximum number of validators allowed.
    /// @param max The maximum number of validators to purchase.
    function purchaseValidators(uint256 max) external;

    /// @notice Sets the operator fee.
    /// @param operatorFeeBps The new operator fee, in basis points.
    function setOperatorFee(uint256 operatorFeeBps) external;

    /// @notice Sets the number of epochs per frame.
    /// @param newEpochsPerFrame The new number of epochs per frame.
    function setEpochsPerFrame(uint256 newEpochsPerFrame) external;

    /// @notice Sets the consensus layer spec.
    /// @param consensusLayerSpec_ The new consensus layer spec.
    function setConsensusLayerSpec(ctypes.ConsensusLayerSpec calldata consensusLayerSpec_) external;

    /// @notice Sets the global validator extra data
    /// @param extraData The new extra data to use
    function setValidatorGlobalExtraData(string calldata extraData) external;

    /// @notice Sets the bounds for reporting.
    /// @param maxAPRUpperBound The maximum APR for the upper bound.
    /// @param maxAPRUpperCoverageBoost The maximum APR for the upper coverage boost.
    /// @param maxRelativeLowerBound The maximum relative value for the lower bound.
    function setReportBounds(uint64 maxAPRUpperBound, uint64 maxAPRUpperCoverageBoost, uint64 maxRelativeLowerBound) external;

    /// @notice Injects ether into the contract.
    function injectEther() external payable;

    /// @notice Voids the given amount of shares.
    /// @param amount The amount of shares to void.
    function voidShares(uint256 amount) external;

    /// @notice Reports the validator data for the given epoch.
    /// @param rprt The consensus layer report to process
    function report(ctypes.ValidatorsReport calldata rprt) external;
}

File 22 of 36 : IMultiPool.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "./IFeeDispatcher.sol";
import "vsuite/interfaces/IvPoolSharesReceiver.sol";

/// @notice PoolStakeDetails contains the details of a stake
/// @param poolId Id of the pool
/// @param ethToPool ETH amount sent to the pool
/// @param ethToIntegrator ETH amount going to the integrator
/// @param pSharesFromPool Amount of pool shares received from the pool
/// @param pSharesFromIntegrator Amount of pool shares received from the integrator
struct PoolStakeDetails {
    uint128 poolId;
    uint128 ethToPool;
    uint128 ethToIntegrator;
    uint128 pSharesFromPool;
    uint128 pSharesFromIntegrator;
}

/// @notice PoolExitDetails contains the details of an exit
/// @param poolId Id of the pool
/// @param exitedPoolShares Amount of pool shares exited
struct PoolExitDetails {
    uint128 poolId;
    uint128 exitedPoolShares;
}

/// @title MultiPool (V1) Interface
/// @author 0xvv @ Kiln
/// @notice This contract contains the common functions to all integration contracts.
///         Contains the functions to add pools, activate/deactivate a pool, change the fee of a pool and change the commission distribution.
interface IMultiPool is IFeeDispatcher, IvPoolSharesReceiver {
    /// @notice Emitted when vPool shares are received
    /// @param vPool Address of the vPool sending the shares
    /// @param poolId Id of the pool in the integrations contract
    /// @param amount The amount of vPool shares received
    event VPoolSharesReceived(address vPool, uint256 poolId, uint256 amount);

    /// @notice Emitted when a vPool in enabled or disabled
    /// @param poolAddress The new pool address
    /// @param id Id of the pool
    /// @param isActive whether the pool can be staked to or not
    event PoolActivation(address poolAddress, uint256 id, bool isActive);

    /// @notice Emitted when a vPool address is added to vPools
    /// @param poolAddress The new pool address
    /// @param id Id of the pool
    event PoolAdded(address poolAddress, uint256 id);

    /// @notice Emitted when the integrator fee is changed
    /// @param poolId Id of the pool
    /// @param operatorFeeBps The new fee in basis points
    event SetFee(uint256 poolId, uint256 operatorFeeBps);

    /// @notice Emitted when the display name is changed
    /// @param name The new name
    event SetName(string name);

    /// @notice Emitted when the display symbol is changed
    /// @param symbol The new display symbol
    event SetSymbol(string symbol);

    /// @notice Emitted when the max commission is set
    /// @param maxCommission The new max commission
    event SetMaxCommission(uint256 maxCommission);

    /// @notice Emitted when the deposits are paused or unpaused
    /// @param isPaused Whether the deposits are paused or not
    event SetDepositsPaused(bool isPaused);

    /// @notice Emitted when staking occurs, contains the details for all the pools
    /// @param staker The address staking
    /// @param depositedEth The amount of ETH staked
    /// @param mintedTokens The amount of integrator shares minted
    /// @param stakeDetails Array of details for each pool, contains the pool id, the amount of ETH sent to the pool,
    ///                     the amount of ETH sent to the integrator, the amount of pool shares received from the pool and
    ///                     the amount of pools shares bought from the integrator
    event Stake(address indexed staker, uint128 depositedEth, uint128 mintedTokens, PoolStakeDetails[] stakeDetails);

    /// @notice Emitted when an exit occurs, contains the details for all the pools
    /// @param staker The address exiting
    /// @param exitedTokens The amount of integrator shares exited
    /// @param exitDetails Array of details for each pool, contains the pool id and the amount of pool shares exited
    event Exit(address indexed staker, uint128 exitedTokens, PoolExitDetails[] exitDetails);

    /// @notice Emitted when the commission is distributed via a manual call
    /// @param poolId Id of the pool
    /// @param shares Amount of pool shares exited
    /// @param weights Array of weights for each recipient
    /// @param recipients Array of recipients
    event ExitedCommissionShares(uint256 indexed poolId, uint256 shares, uint256[] weights, address[] recipients);

    /// @notice Thrown on stake if deposits are paused
    error DepositsPaused();

    /// @notice Thrown when trying to stake but the sum of amounts is not equal to the msg.value
    /// @param sum Sum of amounts in the list
    /// @param msgValue Amount of ETH sent
    error InvalidAmounts(uint256 sum, uint256 msgValue);

    /// @notice Thrown when trying to init the contract without providing a pool address
    error EmptyPoolList();

    /// @notice Thrown when trying to change the fee but there are integrator shares left to sell
    /// @param ethLeft The ETH value of shares left to sell
    /// @param id Id of the pool
    error OutstandingCommission(uint256 ethLeft, uint256 id);

    /// @notice Thrown when trying to add a Pool that is already registered in the contract
    /// @param newPool The pool address
    error PoolAlreadyRegistered(address newPool);

    /// @notice Thrown when trying to deposit to a disabled pool
    /// @param poolId Id of the pool
    error PoolDisabled(uint256 poolId);

    /// @notice Thrown when trying the pool shares callback is called by an address that is not registered
    /// @param poolAddress The pool address
    error NotARegisteredPool(address poolAddress);

    /// @notice Emitted when a pool transfer does not return true.
    /// @param id The id of the pool.
    error PoolTransferFailed(uint256 id);

    /// @notice Thrown when passing an invalid poolId
    /// @param poolId Invalid pool id
    error InvalidPoolId(uint256 poolId);

    /// @notice Thrown when the commission underflow when lowering the fee
    /// @notice To avoid this, the integrator can call exitCommissionShares before lowering the fee or wait for the integrator shares to be sold
    error CommissionPaidUnderflow();

    /// @notice Thrown when minting a null amount of shares
    error ZeroSharesMint();

    /// @notice Thrown when trying to see a fee over the max fee set at initialization
    error FeeOverMax(uint256 maxFeeBps);

    /// @notice Thrown when trying to call the callback outside of the minting process
    error CallbackNotFromMinting();

    /// @notice Thrown when trying to exit the commission shares but there are no shares to exit
    error NoSharesToExit(uint256 poolId);

    /// @notice Returns the list of vPools.
    /// @return vPools The addresses of the pool contract.
    function pools() external view returns (address[] memory vPools);

    /// @notice Returns the current fee in basis points for the given pool.
    /// @return feeBps The current fee in basis points.
    /// @param id Id of the pool
    function getFee(uint256 id) external view returns (uint256 feeBps);

    /// @notice Allows the integrator to change the fee.
    /// @dev Reverts if there are unsold integrator shares.
    /// @param poolId vPool id
    /// @param newFeeBps The new fee in basis points.
    function changeFee(uint256 poolId, uint256 newFeeBps) external;

    /// @notice Allows the admin to change the fee sharing upon withdrawal.
    /// @param recipients The list of fee recipients.
    /// @param splits List of each recipient share in basis points.
    function changeSplit(address[] calldata recipients, uint256[] calldata splits) external;

    /// @notice Allows the integrator to add a vPool.
    /// @dev Reverts if the pool is already in the pools list.
    /// @param newPool The address of the new vPool.
    /// @param fee The fee to be applied to rewards from this vPool, in basis points.
    function addPool(address newPool, uint256 fee) external;

    /// @notice Returns true if the pool is active, false otherwise
    /// @param poolId The id of the vPool.
    function getPoolActivation(uint256 poolId) external view returns (bool);

    /// @notice Returns the ETH value of integrator shares left to sell.
    /// @param poolId The id of the vPool.
    /// @return The ETH value of unsold integrator shares.
    function integratorCommissionOwed(uint256 poolId) external view returns (uint256);

    /// @notice Allows the integrator to exit the integrator shares of a vPool.
    /// @param poolId The id of the vPool.
    function exitCommissionShares(uint256 poolId) external;

    /// @notice Allows the integrator to pause and unpause deposits only.
    /// @param isPaused Whether the deposits are paused or not.
    function pauseDeposits(bool isPaused) external;

    /// @notice Returns true if deposits are paused, false otherwise
    function depositsPaused() external view returns (bool);
}

File 23 of 36 : FeeDispatcher.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/libs/LibErrors.sol";
import "utils.sol/libs/LibUint256.sol";
import "utils.sol/libs/LibConstant.sol";

import "utils.sol/types/array.sol";
import "utils.sol/types/uint256.sol";

import "./interfaces/IFeeDispatcher.sol";

/// @title FeeDispatcher (V1) Contract
/// @author 0xvv @ Kiln
/// @notice This contract contains functions to dispatch the ETH in a contract upon withdrawal.
// slither-disable-next-line naming-convention
abstract contract FeeDispatcher is IFeeDispatcher {
    using LArray for types.Array;
    using LUint256 for types.Uint256;

    /// @dev The recipients of the fees upon withdrawal.
    /// @dev Slot: keccak256(bytes("feeDispatcher.1.feeRecipients")) - 1
    types.Array internal constant $feeRecipients = types.Array.wrap(0xd681f9d3e640a2dd835404271506ef93f020e2fc065878793505e5ea088fde3d);

    /// @dev The splits of each recipient of the fees upon withdrawal.
    /// @dev Slot: keccak256(bytes("feeDispatcher.1.feeSplits")) - 1
    types.Array internal constant $feeSplits = types.Array.wrap(0x31a3fa329157566a07927d0c2ba92ff801e4db8af2ec73f92eaf3e7f78d587a8);

    /// @dev The lock to prevent reentrancy
    /// @dev Slot: keccak256(bytes("feeDispatcher.1.locked")) - 1
    types.Uint256 internal constant $locked = types.Uint256.wrap(0x8472de2bbf04bc62a7ee894bd625126d381bf5e8b726e5cd498c3a9dad76d85b);

    /// @dev The states of the lock, 1 = unlocked, 2 = locked
    uint256 internal constant UNLOCKED = 1;
    uint256 internal constant LOCKED = 2;

    constructor() {
        $locked.set(LOCKED);
    }

    /// @dev An internal function to set the fee split & unlock the reentrancy lock.
    ///      Should be called in the initializer of the inheriting contract.
    // slither-disable-next-line dead-code
    function _initFeeDispatcher(address[] calldata recipients, uint256[] calldata splits) internal {
        _setFeeSplit(recipients, splits);
        $locked.set(UNLOCKED);
    }

    /// @notice Modifier to prevent reentrancy
    modifier nonReentrant() virtual {
        if ($locked.get() == LOCKED) {
            revert Reentrancy();
        }

        $locked.set(LOCKED);

        _;

        $locked.set(UNLOCKED);
    }

    /// @inheritdoc IFeeDispatcher
    // slither-disable-next-line low-level-calls,calls-loop,reentrancy-events,assembly
    function withdrawCommission() external nonReentrant {
        uint256 balance = address(this).balance;
        address[] memory recipients = $feeRecipients.toAddressA();
        uint256[] memory splits = $feeSplits.toUintA();
        for (uint256 i = 0; i < recipients.length;) {
            uint256 share = LibUint256.mulDiv(balance, splits[i], LibConstant.BASIS_POINTS_MAX);
            address recipient = recipients[i];
            emit CommissionWithdrawn(recipient, share);
            (bool success, bytes memory rdata) = recipient.call{value: share}("");
            if (!success) {
                assembly {
                    revert(add(32, rdata), mload(rdata))
                }
            }
            unchecked {
                i++;
            }
        }
    }

    /// @notice Returns the current fee split and recipients
    /// @return feeRecipients The current fee recipients
    /// @return feeSplits  The current fee splits
    /// @dev This function is not pure as it fetches the current fee split and recipients from storage
    function getCurrentSplit() external pure returns (address[] memory, uint256[] memory) {
        return ($feeRecipients.toAddressA(), $feeSplits.toUintA());
    }

    /// @dev Internal utility to set the fee distribution upon withdrawal
    /// @param recipients The new fee recipients list
    /// @param splits The new split between fee recipients
    // slither-disable-next-line dead-code
    function _setFeeSplit(address[] calldata recipients, uint256[] calldata splits) internal {
        if (recipients.length != splits.length) {
            revert UnequalLengths(recipients.length, splits.length);
        }
        $feeSplits.del();
        $feeRecipients.del();
        uint256 sum;
        for (uint256 i = 0; i < recipients.length; i++) {
            uint256 split = splits[i];
            sum += split;
            $feeSplits.toUintA().push(split);
            $feeRecipients.toAddressA().push(recipients[i]);
        }
        if (sum != LibConstant.BASIS_POINTS_MAX) {
            revert LibErrors.InvalidBPSValue();
        }
        emit NewCommissionSplit(recipients, splits);
    }
}

File 24 of 36 : ExitQueueClaimHelper.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/libs/LibErrors.sol";
import "utils.sol/libs/LibUint256.sol";
import "utils.sol/libs/LibConstant.sol";
import "./interfaces/IExitQueueClaimHelper.sol";
import "./interfaces/IFeeDispatcher.sol";

/// @title ExitQueueClaimeHelper (V1) Contract
/// @author gauthiermyr @ Kiln
/// @notice This contract contains functions to resolve and claim casks on several exit queues.
contract ExitQueueClaimHelper is IExitQueueClaimHelper {
    /// @inheritdoc IExitQueueClaimHelper
    function multiResolve(address[] calldata exitQueues, uint256[][] calldata ticketIds)
        external
        view
        override
        returns (int64[][] memory caskIdsOrErrors)
    {
        if (exitQueues.length != ticketIds.length) {
            revert IFeeDispatcher.UnequalLengths(exitQueues.length, ticketIds.length);
        }

        caskIdsOrErrors = new int64[][](exitQueues.length);

        for (uint256 i = 0; i < exitQueues.length;) {
            IvExitQueue exitQueue = IvExitQueue(exitQueues[i]);
            // slither-disable-next-line calls-loop
            caskIdsOrErrors[i] = exitQueue.resolve(ticketIds[i]);

            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IExitQueueClaimHelper
    function multiClaim(address[] calldata exitQueues, uint256[][] calldata ticketIds, uint32[][] calldata casksIds)
        external
        override
        returns (IvExitQueue.ClaimStatus[][] memory statuses)
    {
        if (exitQueues.length != ticketIds.length) {
            revert IFeeDispatcher.UnequalLengths(exitQueues.length, ticketIds.length);
        }
        if (exitQueues.length != casksIds.length) {
            revert IFeeDispatcher.UnequalLengths(exitQueues.length, casksIds.length);
        }

        statuses = new IvExitQueue.ClaimStatus[][](exitQueues.length);

        for (uint256 i = 0; i < exitQueues.length;) {
            IvExitQueue exitQueue = IvExitQueue(exitQueues[i]);
            // slither-disable-next-line calls-loop
            statuses[i] = exitQueue.claim(ticketIds[i], casksIds[i], type(uint16).max);

            unchecked {
                ++i;
            }
        }
    }
}

File 25 of 36 : LibSanitize.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./LibErrors.sol";
import "./LibConstant.sol";

/// @title Lib Sanitize
/// @dev This library helps sanitizing inputs.
library LibSanitize {
    /// @dev Internal utility to sanitize an address and ensure its value is not 0.
    /// @param addressValue The address to verify
    // slither-disable-next-line dead-code
    function notZeroAddress(address addressValue) internal pure {
        if (addressValue == address(0)) {
            revert LibErrors.InvalidZeroAddress();
        }
    }

    /// @dev Internal utility to sanitize an uint256 value and ensure its value is not 0.
    /// @param value The value to verify
    // slither-disable-next-line dead-code
    function notNullValue(uint256 value) internal pure {
        if (value == 0) {
            revert LibErrors.InvalidNullValue();
        }
    }

    /// @dev Internal utility to sanitize a bps value and ensure it's <= 100%.
    /// @param value The bps value to verify
    // slither-disable-next-line dead-code
    function notInvalidBps(uint256 value) internal pure {
        if (value > LibConstant.BASIS_POINTS_MAX) {
            revert LibErrors.InvalidBPSValue();
        }
    }

    /// @dev Internal utility to sanitize a string value and ensure it's not empty.
    /// @param stringValue The string value to verify
    // slither-disable-next-line dead-code
    function notEmptyString(string memory stringValue) internal pure {
        if (bytes(stringValue).length == 0) {
            revert LibErrors.InvalidEmptyString();
        }
    }
}

File 26 of 36 : IAdministrable.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

/// @title Administrable Interface
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice This contract provides all the utilities to handle the administration and its transfer.
interface IAdministrable {
    /// @notice The admin address has been changed.
    /// @param admin The new admin address
    event SetAdmin(address admin);

    /// @notice The pending admin address has been changed.
    /// @param pendingAdmin The pending admin has been changed
    event SetPendingAdmin(address pendingAdmin);

    /// @notice Retrieve the admin address.
    /// @return adminAddress The admin address
    function admin() external view returns (address adminAddress);

    /// @notice Retrieve the pending admin address.
    /// @return pendingAdminAddress The pending admin address
    function pendingAdmin() external view returns (address pendingAdminAddress);

    /// @notice Propose a new admin.
    /// @dev Only callable by the admin
    /// @param _newAdmin The new admin to propose
    function transferAdmin(address _newAdmin) external;

    /// @notice Accept an admin transfer.
    /// @dev Only callable by the pending admin
    function acceptAdmin() external;
}

File 27 of 36 : IFixable.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

/// @title Fixable Interface
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice The Fixable contract can be used on cubs to expose a safe noop to force a fix.
interface IFixable {
    /// @notice Noop method to force a global fix to be applied.
    function fix() external;
}

File 28 of 36 : IFeeDispatcher.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/interfaces/IFixable.sol";

/// @title FeeDispatcher (V1) Interface
/// @author 0xvv @ Kiln
/// @notice This contract contains functions to dispatch the ETH in a contract upon withdrawal.
interface IFeeDispatcher {
    /// @notice Emitted when the commission split is changed.
    /// @param recipients The addresses of recipients
    /// @param splits The percentage of each recipient in basis points
    event NewCommissionSplit(address[] recipients, uint256[] splits);

    /// @notice Emitted when the integrator withdraws ETH
    /// @param withdrawer address withdrawing the ETH
    /// @param amountWithdrawn amount of ETH withdrawn
    event CommissionWithdrawn(address indexed withdrawer, uint256 amountWithdrawn);

    /// @notice Thrown when functions are given lists of different length in batch arguments
    /// @param lengthA First argument length
    /// @param lengthB Second argument length
    error UnequalLengths(uint256 lengthA, uint256 lengthB);

    /// @notice Thrown when a function is called while the contract is locked
    error Reentrancy();

    /// @notice Allows the integrator to withdraw the ETH in the contract.
    function withdrawCommission() external;
}

File 29 of 36 : IvPoolSharesReceiver.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

/// @title Pool Shares Receiver Interface
/// @author mortimr @ Kiln
/// @notice Interface that needs to be implemented for a contract to be able to receive shares
interface IvPoolSharesReceiver {
    /// @notice Callback used by the vPool to notify contracts of shares being transfered
    /// @param operator The address of the operator of the transfer
    /// @param from The address sending the funds
    /// @param amount The amount of shares received
    /// @param data The attached data
    /// @return selector Should return its own selector if everything went well
    function onvPoolSharesReceived(address operator, address from, uint256 amount, bytes memory data) external returns (bytes4 selector);
}

File 30 of 36 : LibErrors.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

library LibErrors {
    error Unauthorized(address account, address expected);
    error InvalidZeroAddress();
    error InvalidNullValue();
    error InvalidBPSValue();
    error InvalidEmptyString();
}

File 31 of 36 : LibUint256.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "prb-math/PRBMath.sol";

library LibUint256 {
    // slither-disable-next-line dead-code
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        // slither-disable-next-line assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @custom:author Vectorized/solady#58681e79de23082fd3881a76022e0842f5c08db8
    // slither-disable-next-line dead-code
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        // slither-disable-next-line assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    // slither-disable-next-line dead-code
    function mulDiv(uint256 a, uint256 b, uint256 c) internal pure returns (uint256) {
        return PRBMath.mulDiv(a, b, c);
    }

    // slither-disable-next-line dead-code
    function ceil(uint256 num, uint256 den) internal pure returns (uint256) {
        return (num / den) + (num % den > 0 ? 1 : 0);
    }
}

File 32 of 36 : LibConstant.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

library LibConstant {
    /// @dev The basis points value representing 100%.
    uint256 internal constant BASIS_POINTS_MAX = 10_000;
    /// @dev The size of a deposit to activate a validator.
    uint256 internal constant DEPOSIT_SIZE = 32 ether;
    /// @dev The minimum freeze timeout before freeze is active.
    uint256 internal constant MINIMUM_FREEZE_TIMEOUT = 100 days;
    /// @dev Address used to represent ETH when an address is required to identify an asset.
    address internal constant ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}

File 33 of 36 : array.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity >=0.8.17;

import "./types.sol";

library LArray {
    // slither-disable-next-line dead-code
    function toUintA(types.Array position) internal pure returns (uint256[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }

    // slither-disable-next-line dead-code
    function toAddressA(types.Array position) internal pure returns (address[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }

    // slither-disable-next-line dead-code
    function toBoolA(types.Array position) internal pure returns (bool[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }

    // slither-disable-next-line dead-code
    function toBytes32A(types.Array position) internal pure returns (bytes32[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }

    // slither-disable-next-line dead-code
    function del(types.Array position) internal {
        // slither-disable-next-line assembly
        assembly {
            let len := sload(position)

            if len {
                // clear the length slot
                sstore(position, 0)

                // calculate the starting slot of the array elements in storage
                mstore(0, position)
                let startPtr := keccak256(0, 0x20)

                for {} len {} {
                    len := sub(len, 1)
                    sstore(add(startPtr, len), 0)
                }
            }
        }
    }

    /// @dev This delete can be used if and only if we only want to clear the length of the array.
    ///         Doing so will create an array that behaves like an empty array in solidity.
    ///         It can have advantages if we often rewrite to the same slots of the array.
    ///         Prefer using `del` if you don't know what you're doing.
    // slither-disable-next-line dead-code
    function dangerousDirtyDel(types.Array position) internal {
        // slither-disable-next-line assembly
        assembly {
            sstore(position, 0)
        }
    }
}

File 34 of 36 : IExitQueueClaimHelper.sol
// SPDX-License-Identifier: BUSL-1.1
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "vsuite/interfaces/IvExitQueue.sol";

/// @title ExitQueueClaimeHelper (V1) Interface
/// @author gauthiermyr @ Kiln
interface IExitQueueClaimHelper {
    /// @notice Resolve a list of casksIds for given exitQueues and tickets
    /// @param exitQueues List of exit queues
    /// @param ticketIds List of tickets in each exit queue
    function multiResolve(address[] calldata exitQueues, uint256[][] calldata ticketIds)
        external
        view
        returns (int64[][] memory caskIdsOrErrors);

    /// @notice Claim caskIds for given tickets on each exit queue
    /// @param exitQueues List of exit queues
    /// @param ticketIds List of tickets in each exit queue
    /// @param casksIds List of caskIds to claim with each ticket
    function multiClaim(address[] calldata exitQueues, uint256[][] calldata ticketIds, uint32[][] calldata casksIds)
        external
        returns (IvExitQueue.ClaimStatus[][] memory statuse);
}

File 35 of 36 : PRBMath.sol
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculating the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE =
        78156646155174841979727994598816262306175212592076161876661_508869554232690281;

    /// FUNCTIONS ///

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    /// @dev Has to use 192.64-bit fixed-point numbers.
    /// See https://ethereum.stackexchange.com/a/96594/24693.
    /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division.
        if (prod1 == 0) {
            unchecked {
                result = prod0 / denominator;
            }
            return result;
        }

        // Make sure the result is less than 2^256. Also prevents denominator == 0.
        if (prod1 >= denominator) {
            revert PRBMath__MulDivOverflow(prod1, denominator);
        }

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0].
        uint256 remainder;
        assembly {
            // Compute remainder using mulmod.
            remainder := mulmod(x, y, denominator)

            // Subtract 256 bit number from 512 bit number.
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
        // See https://cs.stackexchange.com/q/138556/92363.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

                // Divide [prod1 prod0] by lpotdod.
                prod0 := div(prod0, lpotdod)

                // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one.
                lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * lpotdod;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /// @notice Calculates floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within int256.
    ///
    /// @param x The multiplicand as an int256.
    /// @param y The multiplier as an int256.
    /// @param denominator The divisor as an int256.
    /// @return result The result as an int256.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

        // Get hold of the absolute values of x, y and the denominator.
        uint256 ax;
        uint256 ay;
        uint256 ad;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
            ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
        }

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        unchecked {
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

File 36 of 36 : IvExitQueue.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Kiln <[email protected]>
//
// ██╗  ██╗██╗██╗     ███╗   ██╗
// ██║ ██╔╝██║██║     ████╗  ██║
// █████╔╝ ██║██║     ██╔██╗ ██║
// ██╔═██╗ ██║██║     ██║╚██╗██║
// ██║  ██╗██║███████╗██║ ╚████║
// ╚═╝  ╚═╝╚═╝╚══════╝╚═╝  ╚═══╝
//
pragma solidity 0.8.17;

import "utils.sol/interfaces/IFixable.sol";

import "./IvPoolSharesReceiver.sol";
import "../ctypes/ctypes.sol";

/// @title Exit Queue Interface
/// @author mortimr @ Kiln
/// @notice The exit queue stores exit requests until they are filled and claimable
interface IvExitQueue is IFixable, IvPoolSharesReceiver {
    /// @notice Emitted when the stored Pool address is changed
    /// @param pool The new pool address
    event SetPool(address pool);

    /// @notice Emitted when the stored token uri image url is changed
    /// @param tokenUriImageUrl The new token uri image url
    event SetTokenUriImageUrl(string tokenUriImageUrl);

    /// @notice Emitted when the transfer enabled status is changed
    /// @param enabled The new transfer enabled status
    event SetTransferEnabled(bool enabled);

    /// @notice Emitted when the unclaimed funds buffer is changed
    /// @param unclaimedFunds The new unclaimed funds buffer
    event SetUnclaimedFunds(uint256 unclaimedFunds);

    /// @notice Emitted when ether was supplied to the vPool
    /// @param amount The amount of ETH supplied
    event SuppliedEther(uint256 amount);

    /// @notice Emitted when a ticket is created
    /// @param owner The address of the ticket owner
    /// @param idx The index of the ticket
    /// @param id The ID of the ticket
    /// @param ticket The ticket details
    event PrintedTicket(address indexed owner, uint32 idx, uint256 id, ctypes.Ticket ticket);

    /// @notice Emitted when a cask is created
    /// @param id The ID of the cask
    /// @param cask The cask details
    event ReceivedCask(uint32 id, ctypes.Cask cask);

    /// @notice Emitted when a ticket is claimed against a cask, can happen several times for the same ticket but different casks
    /// @param ticketId The ID of the ticket
    /// @param caskId The ID of the cask
    /// @param amountFilled The amount of shares filled
    /// @param amountEthFilled The amount of ETH filled
    /// @param unclaimedEth The amount of ETH that is added to the unclaimed buffer
    event FilledTicket(
        uint256 indexed ticketId, uint32 indexed caskId, uint128 amountFilled, uint256 amountEthFilled, uint256 unclaimedEth
    );

    /// @notice Emitted when a ticket is "reminted" and its external id is modified
    /// @param oldTicketId The old ID of the ticket
    /// @param newTicketId The new ID of the ticket
    /// @param ticketIndex The index of the ticket
    event TicketIdUpdated(uint256 indexed oldTicketId, uint256 indexed newTicketId, uint32 indexed ticketIndex);

    /// @notice Emitted when a payment is made after a user performed a claim
    /// @param recipient The address of the recipient
    /// @param amount The amount of ETH paid
    event Payment(address indexed recipient, uint256 amount);

    /// @notice Transfer of tickets is disabled
    error TransferDisabled();

    /// @notice The provided ticket ID is invalid
    /// @param id The ID of the ticket
    error InvalidTicketId(uint256 id);

    /// @notice The provided cask ID is invalid
    /// @param id The ID of the cask
    error InvalidCaskId(uint32 id);

    /// @notice The provided ticket IDs and cask IDs are not the same length
    error InvalidLengths();

    /// @notice The ticket and cask are not associated
    /// @param ticketId The ID of the ticket
    /// @param caskId The ID of the cask
    error TicketNotMatchingCask(uint256 ticketId, uint32 caskId);

    /// @notice The claim transfer failed
    /// @param recipient The address of the recipient
    /// @param rdata The revert data
    error ClaimTransferFailed(address recipient, bytes rdata);

    enum ClaimStatus {
        CLAIMED,
        PARTIALLY_CLAIMED,
        SKIPPED
    }

    /// @notice Initializes the ExitQueue (proxy pattern)
    /// @param vpool The address of the associated vPool
    /// @param newTokenUriImageUrl The token uri image url
    function initialize(address vpool, string calldata newTokenUriImageUrl) external;

    /// @notice Returns the token uri image url
    /// @return The token uri image url
    function tokenUriImageUrl() external view returns (string memory);

    /// @notice Returns the transfer enabled status
    /// @return True if transfers are enabled
    function transferEnabled() external view returns (bool);

    /// @notice Returns the unclaimed funds buffer
    /// @return The unclaimed funds buffer
    function unclaimedFunds() external view returns (uint256);

    /// @notice Returns the id of the ticket based on the index
    /// @param idx The index of the ticket
    function ticketIdAtIndex(uint32 idx) external view returns (uint256);

    /// @notice Returns the details about the ticket with the provided ID
    /// @param id The ID of the ticket
    /// @return The ticket details
    function ticket(uint256 id) external view returns (ctypes.Ticket memory);

    /// @notice Returns the number of tickets
    /// @return The number of tickets
    function ticketCount() external view returns (uint256);

    /// @notice Returns the details about the cask with the provided ID
    /// @param id The ID of the cask
    /// @return The cask details
    function cask(uint32 id) external view returns (ctypes.Cask memory);

    /// @notice Returns the number of casks
    /// @return The number of casks
    function caskCount() external view returns (uint256);

    /// @notice Resolves the provided tickets to their associated casks or provide resolution error codes
    /// @dev TICKET_ID_OUT_OF_BOUNDS = -1;
    ///      TICKET_ALREADY_CLAIMED = -2;
    ///      TICKET_PENDING = -3;
    /// @param ticketIds The IDs of the tickets to resolve
    /// @return caskIdsOrErrors The IDs of the casks or error codes
    function resolve(uint256[] memory ticketIds) external view returns (int64[] memory caskIdsOrErrors);

    /// @notice Adds eth and creates a new cask
    /// @dev only callbacle by the vPool
    /// @param shares The amount of shares to cover with the provided eth
    function feed(uint256 shares) external payable;

    /// @notice Pulls eth from the unclaimed eth buffer
    /// @dev Only callable by the vPool
    /// @param max The maximum amount of eth to pull
    function pull(uint256 max) external;

    /// @notice Claims the provided tickets against their associated casks
    /// @dev To retrieve the list of casks, an off-chain resolve call should be performed
    /// @param ticketIds The IDs of the tickets to claim
    /// @param caskIds The IDs of the casks to claim against
    /// @param maxClaimDepth The maxiumum recursion depth for the claim, 0 for unlimited
    function claim(uint256[] calldata ticketIds, uint32[] calldata caskIds, uint16 maxClaimDepth)
        external
        returns (ClaimStatus[] memory statuses);

    /// @notice Sets the token uri image inside the returned token uri
    /// @param newTokenUriImageUrl The new token uri image url
    function setTokenUriImageUrl(string calldata newTokenUriImageUrl) external;

    /// @notice Enables or disables transfers of the tickets
    /// @param value True to allow transfers
    function setTransferEnabled(bool value) external;
}

Settings
{
  "remappings": [
    "deploy.sol/=lib/deploy.sol/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/deploy.sol/lib/solmate/src/",
    "vsuite/=lib/vsuite/src/",
    "vsuite.test/=lib/vsuite/test/",
    "prb-math/=lib/vsuite/lib/utils.sol/lib/prb-math/contracts/",
    "utils.sol.test/=lib/vsuite/lib/utils.sol/test/",
    "utils.sol/=lib/vsuite/lib/utils.sol/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"currentVersion","type":"uint256"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CallbackNotFromMinting","type":"error"},{"inputs":[],"name":"CommissionPaidUnderflow","type":"error"},{"inputs":[],"name":"DepositsPaused","type":"error"},{"inputs":[],"name":"EmptyPoolList","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxFeeBps","type":"uint256"}],"name":"FeeOverMax","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"allowance","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"sum","type":"uint256"},{"internalType":"uint256","name":"msgValue","type":"uint256"}],"name":"InvalidAmounts","type":"error"},{"inputs":[],"name":"InvalidBPSValue","type":"error"},{"inputs":[],"name":"InvalidNullValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"InvalidPoolId","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"NoSharesToExit","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"NonZeroPercentageOnDeactivatedPool","type":"error"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"NotARegisteredPool","type":"error"},{"inputs":[{"internalType":"uint256","name":"ethLeft","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"OutstandingCommission","type":"error"},{"inputs":[{"internalType":"uint256","name":"prod1","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath__MulDivOverflow","type":"error"},{"inputs":[{"internalType":"address","name":"newPool","type":"address"}],"name":"PoolAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"PoolDisabled","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"PoolTransferFailed","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"lengthA","type":"uint256"},{"internalType":"uint256","name":"lengthB","type":"uint256"}],"name":"UnequalLengths","type":"error"},{"inputs":[],"name":"ZeroSharesMint","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pSharesSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountSold","type":"uint256"}],"name":"CommissionSharesSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountWithdrawn","type":"uint256"}],"name":"CommissionWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint128","name":"exitedTokens","type":"uint128"},{"components":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"uint128","name":"exitedPoolShares","type":"uint128"}],"indexed":false,"internalType":"struct PoolExitDetails[]","name":"exitDetails","type":"tuple[]"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"recipients","type":"address[]"}],"name":"ExitedCommissionShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"recipients","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"splits","type":"uint256[]"}],"name":"NewCommissionSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"PoolActivation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"SetDepositsPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"operatorFeeBps","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxCommission","type":"uint256"}],"name":"SetMaxCommission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"SetName","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"SetPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"split","type":"uint256[]"}],"name":"SetPoolPercentages","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"SetSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint128","name":"depositedEth","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"mintedTokens","type":"uint128"},{"components":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"uint128","name":"ethToPool","type":"uint128"},{"internalType":"uint128","name":"ethToIntegrator","type":"uint128"},{"internalType":"uint128","name":"pSharesFromPool","type":"uint128"},{"internalType":"uint128","name":"pSharesFromIntegrator","type":"uint128"}],"indexed":false,"internalType":"struct PoolStakeDetails[]","name":"stakeDetails","type":"tuple[]"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VPoolSharesReceived","type":"event"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"_getPool","outputs":[{"internalType":"contract IvPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"feeBps","type":"uint256"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"splits","type":"uint256[]"}],"name":"changeSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"exitCommissionShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCurrentSplit","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPoolActivation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address[]","name":"pools","type":"address[]"},{"internalType":"uint256[]","name":"poolFees","type":"uint256[]"},{"internalType":"address[]","name":"commissionRecipients","type":"address[]"},{"internalType":"uint256[]","name":"commissionDistribution","type":"uint256[]"},{"internalType":"uint256[]","name":"poolPercentages","type":"uint256[]"},{"internalType":"uint256","name":"maxCommissionBps","type":"uint256"},{"internalType":"uint256","name":"monoTicketThreshold","type":"uint256"}],"internalType":"struct Native20Configuration","name":"args","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"integratorCommissionOwed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"exitQueues","type":"address[]"},{"internalType":"uint256[][]","name":"ticketIds","type":"uint256[][]"},{"internalType":"uint32[][]","name":"casksIds","type":"uint32[][]"}],"name":"multiClaim","outputs":[{"internalType":"enum IvExitQueue.ClaimStatus[][]","name":"statuses","type":"uint8[][]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"exitQueues","type":"address[]"},{"internalType":"uint256[][]","name":"ticketIds","type":"uint256[][]"}],"name":"multiResolve","outputs":[{"internalType":"int64[][]","name":"caskIdsOrErrors","type":"int64[][]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onvPoolSharesReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused","type":"bool"}],"name":"pauseDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"requestExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minTicketEthValue","type":"uint256"}],"name":"setMonoTicketThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"bool","name":"status","type":"bool"},{"internalType":"uint256[]","name":"newPoolPercentages","type":"uint256[]"}],"name":"setPoolActivation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"split","type":"uint256[]"}],"name":"setPoolPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnderlyingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawCommission","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506200005060027f8472de2bbf04bc62a7ee894bd625126d381bf5e8b726e5cd498c3a9dad76d85b60001b6200009560201b620017c71790919060201c565b6200008f6000197fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7660001b6200009560201b620017c71790919060201c565b62000099565b9055565b61475180620000a96000396000f3fe6080604052600436106101f95760003560e01c806360da3e831161010d578063a81d04e7116100a0578063e3ca2d651161006f578063e3ca2d65146105a2578063ef5ca789146105c2578063f851a440146105e5578063fcee45f4146105fa578063fdc3a3d51461063457600080fd5b8063a81d04e714610506578063b584686914610533578063b7ba18c714610553578063c5c51dca1461058057600080fd5b806375829def116100dc57806375829def14610478578063854e0c6f1461049857806395d89b41146104b85780639b21cf6c146104cd57600080fd5b806360da3e83146103f357806370a0823114610418578063721c651314610438578063738b62e51461045857600080fd5b80632c4e722e116101905780633af9e6691161015f5780633af9e6691461035e5780633e25e8371461037e5780634538e0091461039357806349d6ed56146103b35780634da05606146103d357600080fd5b80632c4e722e14610305578063313ce5671461031a57806332a9caba146103365780633a4b66f11461035657600080fd5b80631468f9ef116101cc5780631468f9ef1461028357806318160ddd146102a3578063235b64c2146102b857806326782247146102d857600080fd5b806306fdde03146101fe5780630e18b681146102295780630eb6f8d914610240578063143a08d414610260575b600080fd5b34801561020a57600080fd5b50610213610654565b604051610220919061389a565b60405180910390f35b34801561023557600080fd5b5061023e6106a3565b005b34801561024c57600080fd5b5061023e61025b366004613906565b610712565b34801561026c57600080fd5b50610275610772565b604051908152602001610220565b34801561028f57600080fd5b5061023e61029e366004613961565b610781565b3480156102af57600080fd5b506102756107b6565b3480156102c457600080fd5b5061023e6102d33660046139c0565b6107c0565b3480156102e457600080fd5b506102ed6107f5565b6040516001600160a01b039091168152602001610220565b34801561031157600080fd5b506102756107ff565b34801561032657600080fd5b5060405160128152602001610220565b34801561034257600080fd5b5061023e6103513660046139ee565b610842565b61023e610879565b34801561036a57600080fd5b50610275610379366004613a1a565b61088b565b34801561038a57600080fd5b5061023e61089c565b34801561039f57600080fd5b5061023e6103ae366004613a37565b610ae4565b3480156103bf57600080fd5b506102756103ce3660046139c0565b610ea5565b3480156103df57600080fd5b5061023e6103ee3660046139c0565b610eb0565b3480156103ff57600080fd5b50610408610ee2565b6040519015158152602001610220565b34801561042457600080fd5b50610275610433366004613a1a565b610f0c565b34801561044457600080fd5b5061023e6104533660046139c0565b610f37565b34801561046457600080fd5b5061023e610473366004613a72565b610f40565b34801561048457600080fd5b5061023e610493366004613a1a565b610fc7565b3480156104a457600080fd5b5061023e6104b3366004613a8f565b610ff9565b3480156104c457600080fd5b5061021361102c565b3480156104d957600080fd5b506104ed6104e8366004613b16565b611057565b6040516001600160e01b03199091168152602001610220565b34801561051257600080fd5b50610526610521366004613961565b611197565b6040516102209190613bd9565b34801561053f57600080fd5b5061040861054e3660046139c0565b61130b565b34801561055f57600080fd5b5061057361056e366004613c66565b611339565b6040516102209190613cff565b34801561058c57600080fd5b50610595611505565b6040516102209190613dea565b3480156105ae57600080fd5b5061023e6105bd366004613dfd565b6115d1565b3480156105ce57600080fd5b506105d7611699565b604051610220929190613e4f565b3480156105f157600080fd5b506102ed61176c565b34801561060657600080fd5b506102756106153660046139c0565b600090815260008051602061463c833981519152602052604090205490565b34801561064057600080fd5b506102ed61064f3660046139c0565b611776565b606061067f7feee152275d096301850a53ae85c6991c818bc6bac8a2174c268aa94ed7cf06f16117cb565b60405160200161068f9190613e7d565b604051602081830303815290604052905090565b6106ab611863565b6001600160a01b0316336001600160a01b0316146106fd57336106cc611863565b60405163295a81c160e01b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b6107063361188d565b61071060006118fb565b565b61071a611960565b6001600160a01b0316336001600160a01b03161461073b57336106cc611960565b61074683151561198a565b600085815260008051602061461c833981519152602052604090205561076c8282611994565b50505050565b600061077c611b46565b905090565b610789611960565b6001600160a01b0316336001600160a01b0316146107aa57336106cc611960565b61076c84848484611b7d565b600061077c611d30565b6107c8611960565b6001600160a01b0316336001600160a01b0316146107e957336106cc611960565b6107f281611d48565b50565b600061077c611863565b60008061080a611d30565b90506000811161082257670de0b6b3a764000061083c565b61083c61082d611b46565b670de0b6b3a764000083611d71565b91505090565b61084a611960565b6001600160a01b0316336001600160a01b03161461086b57336106cc611960565b6108758282611d88565b5050565b61088234611f2a565b6107f234611f4b565b6000610896826125dc565b92915050565b60026108b46000805160206146fc8339815191525490565b036108d25760405163558a1e0360e11b815260040160405180910390fd5b60026000805160206146fc8339815191525547600060008051602061459c83398151915280548060200260200160405190810160405280929190818152602001828054801561094a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161092c575b50505050509050600061096b60008051602061465c83398151915260001b90565b8054806020026020016040519081016040528092919081815260200182805480156109b557602002820191906000526020600020905b8154815260200190600101908083116109a1575b5050505050905060005b8251811015610ac85760006109f0858484815181106109e0576109e0613e99565b6020026020010151612710611d71565b90506000848381518110610a0657610a06613e99565b60200260200101519050806001600160a01b03167fd244b5a3b2e3977ecffe1a5e5ab7661aadfecbae24be711b7a72bb42bd1b2db083604051610a4b91815260200190565b60405180910390a2600080826001600160a01b03168460405160006040518083038185875af1925050503d8060008114610aa1576040519150601f19603f3d011682016040523d82523d6000602084013e610aa6565b606091505b509150915081610ab857805181602001fd5b5050600190920191506109bf9050565b505060016000805160206146fc83398151915255506107109050565b6000610b0e7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b8103610e5a57610b46610b22826001613ec5565b7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7655565b7f91efa3d50feccde0d0d202f8ae5c41ca0b2be614cebcb2bd2f4b019396e6568a81600036604051610b7a93929190613f01565b60405180910390a1610bec610b8f8380613f1b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152507feee152275d096301850a53ae85c6991c818bc6bac8a2174c268aa94ed7cf06f1939250506126489050565b7f4df9dcd34ae35f40f2c756fd8ac83210ed0b76d065543ee73d868aec7c7fcf02610c178380613f1b565b604051610c25929190613f61565b60405180910390a1610c9a610c3d6020840184613f1b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152507f4a8b3e24ebc795477af927068865c6fcc26e359a994edca2492e515a46aad711939250506126489050565b7fadf3ae8bd543b3007d464f15cb8ea1db3f44e84d41d203164f40b95e27558ac6610cc86020840184613f1b565b604051610cd6929190613f61565b60405180910390a1610cf6610cf16060840160408501613a1a565b61188d565b610d036060830183613f75565b9050600003610d255760405163e613e6d960e01b815260040160405180910390fd5b610d326080830183613f75565b9050610d416060840184613f75565b905014610d8757610d556060830183613f75565b9050610d646080840184613f75565b604051635020e50560e01b815260048101939093526024830152506044016106f4565b60005b610d976060840184613f75565b9050811015610e0657610dfe610db06060850185613f75565b83818110610dc057610dc0613e99565b9050602002016020810190610dd59190613a1a565b610de26080860186613f75565b84818110610df257610df2613e99565b90506020020135611d88565b600101610d8a565b50610e1c610e1760e0840184613f75565b611994565b610e3e610e2c60a0840184613f75565b610e3960c0860186613f75565b612654565b610e4c826101000135612677565b610875826101200135611d48565b80610e837fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b60405163031b997760e51b8152600481019290925260248201526044016106f4565b6000610896826126de565b610eb8611960565b6001600160a01b0316336001600160a01b031614610ed957336106cc611960565b6107f281612725565b600061077c7fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e75490565b6001600160a01b038116600090815260008051602061467c8339815191526020526040812054610896565b6107f281612a3f565b610f48611960565b6001600160a01b0316336001600160a01b031614610f6957336106cc611960565b60405181151581527f0797bb255611d0d3d24d654500ba8d9fbb79fd62d6d636a3aafa8bf3d5d6c6709060200160405180910390a16107f27fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e7829055565b610fcf611960565b6001600160a01b0316336001600160a01b031614610ff057336106cc611960565b6107f2816118fb565b611001611960565b6001600160a01b0316336001600160a01b03161461102257336106cc611960565b6108758282611994565b606061067f7f4a8b3e24ebc795477af927068865c6fcc26e359a994edca2492e515a46aad7116117cb565b60008061106333612e15565b600081815260008051602061461c83398151915260205260409020549091506001908116146110a85760405163ca5d071f60e01b8152600481018290526024016106f4565b6001600160a01b0386166110d86000805160206145bc8339815191525b6000848152602091909152604090205490565b6001600160a01b03161415806110f657506001600160a01b03851615155b156111145760405163aea5cf9160e01b815260040160405180910390fd5b60008181526000805160206146dc83398151915260205260408120805486929061113f908490613ec5565b909155505060408051338152602081018390529081018590527ff5833c90fcf127b08719749581fc368b15b6e3e8fd79b1dbc2fb14b7ca6e0ccc9060600160405180910390a1506326c873db60e21b95945050505050565b60608382146111c357604051635020e50560e01b815260048101859052602481018390526044016106f4565b836001600160401b038111156111db576111db613ad0565b60405190808252806020026020018201604052801561120e57816020015b60608152602001906001900390816111f95790505b50905060005b8481101561130257600086868381811061123057611230613e99565b90506020020160208101906112459190613a1a565b9050806001600160a01b031663f8c2153586868581811061126857611268613e99565b905060200281019061127a9190613f75565b6040518363ffffffff1660e01b8152600401611297929190613ff0565b600060405180830381865afa1580156112b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112dc9190810190614027565b8383815181106112ee576112ee613e99565b602090810291909101015250600101611214565b50949350505050565b600061089660008051602061461c8339815191525b6000848152602091909152604090205460019081161490565b606085841461136557604051635020e50560e01b815260048101879052602481018590526044016106f4565b85821461138f57604051635020e50560e01b815260048101879052602481018390526044016106f4565b856001600160401b038111156113a7576113a7613ad0565b6040519080825280602002602001820160405280156113da57816020015b60608152602001906001900390816113c55790505b50905060005b868110156114fa5760008888838181106113fc576113fc613e99565b90506020020160208101906114119190613a1a565b9050806001600160a01b031663adcf116388888581811061143457611434613e99565b90506020028101906114469190613f75565b88888781811061145857611458613e99565b905060200281019061146a9190613f75565b61ffff6040518663ffffffff1660e01b815260040161148d9594939291906140cc565b6000604051808303816000875af11580156114ac573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114d49190810190614142565b8383815181106114e6576114e6613e99565b6020908102919091010152506001016113e0565b509695505050505050565b6060600061151f6000805160206146bc8339815191525490565b90506000816001600160401b0381111561153b5761153b613ad0565b604051908082528060200260200182016040528015611564578160200160208202803683370190505b50905060005b828110156115ca576115986000805160206145bc8339815191525b6000838152602091909152604090205490565b8282815181106115aa576115aa613e99565b6001600160a01b039092166020928302919091019091015260010161156a565b5092915050565b6115d9611960565b6001600160a01b0316336001600160a01b0316146115fa57336106cc611960565b600061160583612e92565b90506116118284612f64565b600061161c84612e92565b60008581526000805160206145dc83398151915260205260408120549192506116458383613ec5565b9050838110156116685760405163cdd14d1b60e01b815260040160405180910390fd5b61167284826141d5565b6000805160206145dc83398151915260009788526020526040909620959095555050505050565b60608060008051602061459c83398151915260008051602061465c83398151915281546040805160208084028201810190925282815291849183018282801561170b57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116116ed575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561175d57602002820191906000526020600020905b815481526020019060010190808311611749575b50505050509050915091509091565b600061077c611960565b600061178e6000805160206146bc8339815191525490565b82106117b05760405163d531737d60e01b8152600481018390526024016106f4565b6108966000805160206145bc8339815191526110c5565b9055565b8054606090829081906117dd906141e8565b80601f0160208091040260200160405190810160405280929190818152602001828054611809906141e8565b80156118565780601f1061182b57610100808354040283529160200191611856565b820191906000526020600020905b81548152906001019060200180831161183957829003601f168201915b5050505050915050919050565b600061077c7f3c1eebcc225c6cc7f5f8765767af6eff617b4139dc3624923a2db67dbca7b68e5490565b61189681613028565b6040516001600160a01b03821681527f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19060200160405180910390a16107f27f927a17e5ea75d9461748062a2652f4d3698a628896c9832f8488fa0d2846af09829055565b6040516001600160a01b03821681527f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f49060200160405180910390a16107f27f3c1eebcc225c6cc7f5f8765767af6eff617b4139dc3624923a2db67dbca7b68e829055565b600061077c7f927a17e5ea75d9461748062a2652f4d3698a628896c9832f8488fa0d2846af095490565b6000811515610896565b6000805160206146bc8339815191525481146119e357806119c16000805160206146bc8339815191525490565b604051635020e50560e01b8152600481019290925260248201526044016106f4565b60006119fc6000805160206145fc83398151915261304f565b6000805160206145fc83398151915260005b83811015611ae4576000611a2f60008051602061461c833981519152611320565b90506000868684818110611a4557611a45613e99565b90506020020135905081158015611a5b57508015155b15611a7c576040516303b1bb5d60e31b8152600481018490526024016106f4565b868684818110611a8e57611a8e613e99565b9050602002013585611aa09190613ec5565b945083878785818110611ab557611ab5613e99565b835460018101855560009485526020948590209490910292909201359290910191909155505050600101611a0e565b506127108214611b0757604051630a68b9d760e41b815260040160405180910390fd5b7fb73c61a2aeb51508c0c5d9bab21439a39e825916565b3210eddb7310a64e874e8484604051611b38929190613ff0565b60405180910390a150505050565b600080805b6000805160206146bc83398151915254811015611b7757611b6b81613081565b90910190600101611b4b565b50919050565b828114611ba757604051635020e50560e01b815260048101849052602481018290526044016106f4565b611bbe60008051602061465c83398151915261304f565b611bd560008051602061459c83398151915261304f565b6000805b84811015611cc9576000848483818110611bf557611bf5613e99565b9050602002013590508083611c0a9190613ec5565b60008051602061465c83398151915280546001810182556000919091527f7f4138ba837fd9f0217ae6ee377e4094fe6493a1ee263ca7f57cde5da5a7977b01829055925060008051602061459c833981519152878784818110611c6f57611c6f613e99565b9050602002016020810190611c849190613a1a565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b039092169190911790555080611cc18161421c565b915050611bd9565b506127108114611cec57604051630a68b9d760e41b815260040160405180910390fd5b7fbf29864d443bca1c8bf52ec7006503a5e58e06535e2eea7bc37651f7127e376185858585604051611d219493929190614235565b60405180910390a15050505050565b600061077c60008051602061469c8339815191525490565b6107f27f900053b761278bb5de4eeaea5ed9000b89943edad45dcf64a9dab96d0ce29c2e829055565b6000611d7e84848461309f565b90505b9392505050565b611d918161316c565b611d9a82613028565b6000611db26000805160206146bc8339815191525490565b905060005b81811015611e1a57611dd66000805160206145bc833981519152611585565b6001600160a01b0316846001600160a01b031603611e12576040516301b6ee3960e71b81526001600160a01b03851660048201526024016106f4565b600101611db7565b50611e2d836001600160a01b031661318f565b60008281526000805160206145bc833981519152602090815260408083209390935560008051602061463c833981519152905220829055611e6e600161198a565b600082815260008051602061461c8339815191526020526040902055611eaa611e98826001613ec5565b6000805160206146bc83398151915255565b604080516001600160a01b0385168152602081018390527f0c98febfffcec480c66a977e13f14bafdb5199ea9603591a0715b0cabe0c3ae2910160405180910390a160408051828152602081018490527f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5910160405180910390a1505050565b806000036107f25760405163095e705160e11b815260040160405180910390fd5b6000611f757fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e75490565b15611f935760405163deeb694360e01b815260040160405180910390fd5b60006000805160206145fc833981519152805480602002602001604051908101604052809291908181526020018280548015611fee57602002820191906000526020600020905b815481526020019060010190808311611fda575b50505050509050600081516001600160401b0381111561201057612010613ad0565b60405190808252806020026020018201604052801561206957816020015b6040805160a08101825260008082526020808301829052928201819052606082018190526080820152825260001990920191018161202e5790505b5090506000805b6000805160206146bc8339815191525481101561258b57600084828151811061209b5761209b613e99565b6020026020010151111561258357808382815181106120bc576120bc613e99565b6020026020010151600001906001600160801b031690816001600160801b03168152505060006120f8878684815181106109e0576109e0613e99565b9050612103826131a0565b600061210e83611776565b9050600061211a611d30565b90506000612126611b46565b9050655af3107a400082101561226d5760008581527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a602052604081208054869290612173908490613ec5565b925050819055506000836001600160a01b031663d0e30db0866040518263ffffffff1660e01b815260040160206040518083038185885af11580156121bc573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906121e19190614298565b90506121ed8188613ec5565b96506121f9338261320e565b8488878151811061220c5761220c613e99565b6020026020010151602001906001600160801b031690816001600160801b0316815250508088878151811061224357612243613e99565b6020026020010151606001906001600160801b031690816001600160801b0316815250505061257e565b6000612278866126de565b90506000633b9aca008211156124025781861886831102821861229b81886141d5565b965060006122e0826122ac8b6132a5565b60008c81526000805160206146dc83398151915260205260409020546122d291906141d5565b6122db8c613081565b611d71565b9050816000805160206145dc83398151915260008b815260200190815260200160002060008282546123129190613ec5565b92505081905550818b8a8151811061232c5761232c613e99565b6020026020010151604001906001600160801b031690816001600160801b031681525050808b8a8151811061236357612363613e99565b6020908102919091018101516001600160801b03909216608090920191909152604080518381529182018b905281018390527ffdb2feef2c30ce94651104212914c3249c15af2822e625dac7712175f6fce88e9060600160405180910390a160006123cf838888611d71565b9050806000036123f25760405163d7eb52ab60e01b815260040160405180910390fd5b6123fc8185613ec5565b93505050505b85156125655760008781527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a602052604081208054889290612445908490613ec5565b925050819055506000856001600160a01b031663d0e30db0886040518263ffffffff1660e01b815260040160206040518083038185885af115801561248e573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906124b39190614298565b905060006124c2888787611d71565b9050806000036124e55760405163d7eb52ab60e01b815260040160405180910390fd5b878b8a815181106124f8576124f8613e99565b602002602001015160200181815161251091906142b1565b6001600160801b03169052508a5182908c908b90811061253257612532613e99565b602002602001015160600181815161254a91906142b1565b6001600160801b03169052506125608184613ec5565b925050505b61256f338261320e565b6125798189613ec5565b975050505b505050505b600101612070565b50336001600160a01b03167f22064e3ba88064d71ff211d550f2b1f3e63b19eb3fd647544e21c8f6e5ccc42e8683856040516125c9939291906142d1565b60405180910390a2506001949350505050565b6000806125e7611b46565b905060006125f3611d30565b9050811580612600575080155b1561260f575060009392505050565b6001600160a01b038416600090815260008051602061467c8339815191526020526040902054612640908383611d71565b949350505050565b818061076c83826143b3565b61266084848484611b7d565b60016000805160206146fc8339815191525561076c565b6126808161316c565b6126a97f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca8785829055565b6040518181527ed422a7205dd59ab69db2ebeb5699ccb6e9e5eed515043c5e4677221986ad3b9060200160405180910390a150565b6000806126ea83612e92565b60008481526000805160206145dc83398151915260205260409020549091508082111561271b5761264081836141d5565b5060009392505050565b6000805160206146bc8339815191525481106127575760405163d531737d60e01b8152600481018290526024016106f4565b6000612762826132a5565b9050806000036127885760405163280d7c2760e11b8152600481018390526024016106f4565b600060008051602061459c8339815191528054806020026020016040519081016040528092919081815260200182805480156127ed57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116127cf575b50505050509050600061280e60008051602061465c83398151915260001b90565b80548060200260200160405190810160405280929190818152602001828054801561285857602002820191906000526020600020905b815481526020019060010190808311612844575b50505050509050600061286a85611776565b905060005b83518110156128c6576000612890868584815181106109e0576109e0613e99565b905080156128bd576128bd8782858886815181106128b0576128b0613e99565b60200260200101516133a6565b5060010161286f565b5061298f84826001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612909573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292d9190614298565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561296b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db9190614298565b60008681527f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b6020526040812080549091906129cc908490613ec5565b909155506129db905085612e92565b60008681526000805160206145dc833981519152602052604090819020919091555185907faf5592e0f02600cde237bef50ea4c3508bd3d392bcfc5683b03c1af1c341794b90612a3090879086908890614472565b60405180910390a25050505050565b6000612a5760008051602061469c8339815191525490565b90506000612a63611b46565b9050612a6f3384613506565b6000612a7c848385611d71565b90506000612a966000805160206146bc8339815191525490565b905080600103612b5b57604080516001808252818301909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081612ab7579050509050612b0660008483600081518110612af957612af9613e99565b60200260200101516135e1565b612b106000613720565b336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038783604051612b4b92919061449d565b60405180910390a2505050505050565b60006000805160206145fc833981519152805480602002602001604051908101604052809291908181526020018280548015612bb657602002820191906000526020600020905b815481526020019060010190808311612ba2575b50507f900053b761278bb5de4eeaea5ed9000b89943edad45dcf64a9dab96d0ce29c2e54939450612be692505050565b831015612d125760008060005b84811015612c56576000612c13888684815181106109e0576109e0613e99565b90506000612c2083613081565b90506000612c2e8383614504565b9050888210158015612c3f57508581135b15612c4b578095508394505b505050600101612bf3565b506000821315612d0f57604080516001808252818301909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081612c77579050509050612cb8828783600081518110612af957612af9613e99565b612cc182613720565b336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038a83604051612cfc92919061449d565b60405180910390a2505050505050505050565b50505b6000826001600160401b03811115612d2c57612d2c613ad0565b604051908082528060200260200182016040528015612d7157816020015b6040805180820190915260008082526020820152815260200190600190039081612d4a5790505b50905060005b83811015612dc8576000612d97868584815181106109e0576109e0613e99565b90508015612db657612db68282858581518110612af957612af9613e99565b612dbf82613720565b50600101612d77565b50336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038883604051612e0492919061449d565b60405180910390a250505050505050565b6000805b6000805160206146bc83398151915254811015612e6d57612e476000805160206145bc833981519152611585565b6001600160a01b0316836001600160a01b031603612e655792915050565b600101612e19565b50604051639823215960e01b81526001600160a01b03831660048201526024016106f4565b600080612e9e83613762565b60008481527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a60209081526040808320547f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b9092529091205491925090612f058184613ec5565b8210612f1657506000949350505050565b600082612f238386613ec5565b612f2d91906141d5565b9050612f5a8160008051602061463c83398151915260008981526020919091526040902054612710611d71565b9695505050505050565b7f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca878554821115612fca577f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca878554604051627431e360e31b81526004016106f491815260200190565b8160008051602061463c833981519152600083815260209182526040908190209290925581518381529081018490527f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5910160405180910390a15050565b6001600160a01b0381166107f25760405163f6b2911f60e01b815260040160405180910390fd5b8054801561087557600082558160005260206000205b811561307c57600182039150600082820155613065565b505050565b600061308c826126de565b61309583613762565b61089691906141d5565b60008080600019858709858702925082811083820303915050806000036130d9578382816130cf576130cf614524565b0492505050611d81565b83811061310357604051631dcf306360e21b815260048101829052602481018590526044016106f4565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6127108111156107f257604051630a68b9d760e41b815260040160405180910390fd5b60006001600160a01b038216610896565b6000805160206146bc8339815191525481106131d25760405163d531737d60e01b8152600481018290526024016106f4565b60006131eb60008051602061461c833981519152611320565b9050806108755760405163ca5d071f60e01b8152600481018390526024016106f4565b6132448161322860008051602061469c8339815191525490565b6132329190613ec5565b60008051602061469c83398151915255565b6001600160a01b038216600081815260008051602061467c83398151915260209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60008181526000805160206145bc83398151915260205260408120546000816001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190614298565b9050801561271b576133a1613339856126de565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613377573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061339b9190614298565b83611d71565b612640565b60008481526000805160206146dc8339815191526020526040812080548592906133d19084906141d5565b925050819055506000826001600160a01b0316637f9654f5846001600160a01b031663ffed4bf56040518163ffffffff1660e01b8152600401602060405180830381865afa158015613427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061344b919061453a565b6040516bffffffffffffffffffffffff19606087901b16602082015287906034016040516020818303038152906040526040518463ffffffff1660e01b815260040161349993929190614557565b6020604051808303816000875af11580156134b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134dc919061457e565b9050806134ff5760405163a10785df60e01b8152600481018690526024016106f4565b5050505050565b6001600160a01b038216600090815260008051602061467c8339815191526020526040902054808211156135575760405163cf47918160e01b815260048101839052602481018290526044016106f4565b61357b8261357160008051602061469c8339815191525490565b61323291906141d5565b81810360008051602061467c8339815191526001600160a01b03851660008181526020928352604080822094909455925185815290917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60006135ec84611776565b9050600061369484836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613632573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136569190614298565b846001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561296b573d6000803e3d6000fd5b905060006136a186613762565b6001600160801b0380841660208701528716855290506136c3868385336133a6565b6136cc86613762565b6136d690826141d5565b60008781527f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b602052604081208054909190613713908490613ec5565b9091555050505050505050565b60008181526000805160206146dc833981519152602052604090205461374a90600a612710611d71565b613753826132a5565b11156107f2576107f281612725565b60008061376e83611776565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d49190614298565b9050806000036137e8575060009392505050565b6126406000805160206146dc833981519152600086815260200190815260200160002054836001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613377573d6000803e3d6000fd5b60005b8381101561386557818101518382015260200161384d565b50506000910152565b6000815180845261388681602086016020860161384a565b601f01601f19169290920160200192915050565b602081526000611d81602083018461386e565b80151581146107f257600080fd5b60008083601f8401126138cd57600080fd5b5081356001600160401b038111156138e457600080fd5b6020830191508360208260051b85010111156138ff57600080fd5b9250929050565b6000806000806060858703121561391c57600080fd5b84359350602085013561392e816138ad565b925060408501356001600160401b0381111561394957600080fd5b613955878288016138bb565b95989497509550505050565b6000806000806040858703121561397757600080fd5b84356001600160401b038082111561398e57600080fd5b61399a888389016138bb565b909650945060208701359150808211156139b357600080fd5b50613955878288016138bb565b6000602082840312156139d257600080fd5b5035919050565b6001600160a01b03811681146107f257600080fd5b60008060408385031215613a0157600080fd5b8235613a0c816139d9565b946020939093013593505050565b600060208284031215613a2c57600080fd5b8135611d81816139d9565b600060208284031215613a4957600080fd5b81356001600160401b03811115613a5f57600080fd5b82016101408185031215611d8157600080fd5b600060208284031215613a8457600080fd5b8135611d81816138ad565b60008060208385031215613aa257600080fd5b82356001600160401b03811115613ab857600080fd5b613ac4858286016138bb565b90969095509350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613b0e57613b0e613ad0565b604052919050565b60008060008060808587031215613b2c57600080fd5b8435613b37816139d9565b9350602085810135613b48816139d9565b93506040860135925060608601356001600160401b0380821115613b6b57600080fd5b818801915088601f830112613b7f57600080fd5b813581811115613b9157613b91613ad0565b613ba3601f8201601f19168501613ae6565b91508082528984828501011115613bb957600080fd5b808484018584013760008482840101525080935050505092959194509250565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b83811015613c5857888603603f19018552825180518088529088019088880190845b81811015613c4257835160070b8352928a0192918a0191600101613c23565b5090975050509386019391860191600101613c01565b509398975050505050505050565b60008060008060008060608789031215613c7f57600080fd5b86356001600160401b0380821115613c9657600080fd5b613ca28a838b016138bb565b90985096506020890135915080821115613cbb57600080fd5b613cc78a838b016138bb565b90965094506040890135915080821115613ce057600080fd5b50613ced89828a016138bb565b979a9699509497509295939492505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613d9957878503603f1901845281518051808752908701908787019060005b81811015613d8357835160038110613d7157634e487b7160e01b600052602160045260246000fd5b83529289019291890191600101613d49565b5090965050509285019290850190600101613d26565b5092979650505050505050565b600081518084526020808501945080840160005b83811015613ddf5781516001600160a01b031687529582019590820190600101613dba565b509495945050505050565b602081526000611d816020830184613da6565b60008060408385031215613e1057600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b83811015613ddf57815187529582019590820190600101613e33565b604081526000613e626040830185613da6565b8281036020840152613e748185613e1f565b95945050505050565b60008251613e8f81846020870161384a565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561089657610896613eaf565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b838152604060208201526000613e74604083018486613ed8565b6000808335601e19843603018112613f3257600080fd5b8301803591506001600160401b03821115613f4c57600080fd5b6020019150368190038213156138ff57600080fd5b602081526000611d7e602083018486613ed8565b6000808335601e19843603018112613f8c57600080fd5b8301803591506001600160401b03821115613fa657600080fd5b6020019150600581901b36038213156138ff57600080fd5b81835260006001600160fb1b03831115613fd757600080fd5b8260051b80836020870137939093016020019392505050565b602081526000611d7e602083018486613fbe565b60006001600160401b0382111561401d5761401d613ad0565b5060051b60200190565b6000602080838503121561403a57600080fd5b82516001600160401b0381111561405057600080fd5b8301601f8101851361406157600080fd5b805161407461406f82614004565b613ae6565b81815260059190911b8201830190838101908783111561409357600080fd5b928401925b828410156140c15783518060070b81146140b25760008081fd5b82529284019290840190614098565b979650505050505050565b6060815260006140e0606083018789613fbe565b828103602084810191909152858252869181016000805b8881101561412657843563ffffffff8116808214614113578384fd5b84525093830193918301916001016140f7565b505080935050505061ffff831660408301529695505050505050565b6000602080838503121561415557600080fd5b82516001600160401b0381111561416b57600080fd5b8301601f8101851361417c57600080fd5b805161418a61406f82614004565b81815260059190911b820183019083810190878311156141a957600080fd5b928401925b828410156140c1578351600381106141c65760008081fd5b825292840192908401906141ae565b8181038181111561089657610896613eaf565b600181811c908216806141fc57607f821691505b602082108103611b7757634e487b7160e01b600052602260045260246000fd5b60006001820161422e5761422e613eaf565b5060010190565b6040808252810184905260008560608301825b8781101561427857823561425b816139d9565b6001600160a01b0316825260209283019290910190600101614248565b50838103602085015261428c818688613fbe565b98975050505050505050565b6000602082840312156142aa57600080fd5b5051919050565b6001600160801b038181168382160190808211156115ca576115ca613eaf565b600060608083016001600160801b03808816855260208188168187015260408481880152838851808652608095508589019150838a0160005b8281101561435357815180518816855286810151881687860152858101518816868601528981015188168a86015288015187168885015260a0909301929085019060010161430a565b50919c9b505050505050505050505050565b601f82111561307c57600081815260208120601f850160051c8101602086101561438c5750805b601f850160051c820191505b818110156143ab57828155600101614398565b505050505050565b81516001600160401b038111156143cc576143cc613ad0565b6143e0816143da84546141e8565b84614365565b602080601f83116001811461441557600084156143fd5750858301515b600019600386901b1c1916600185901b1785556143ab565b600085815260208120601f198616915b8281101561444457888601518255948401946001909101908401614425565b50858210156144625787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b83815260606020820152600061448b6060830185613e1f565b8281036040840152612f5a8185613da6565b600060408083016001600160801b03808716855260208381870152828751808552606088019150828901945060005b818110156144f557855180518616845284015185168484015294830194918601916001016144cc565b50909998505050505050505050565b81810360008312801583831316838312821617156115ca576115ca613eaf565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561454c57600080fd5b8151611d81816139d9565b60018060a01b0384168152826020820152606060408201526000613e74606083018461386e565b60006020828403121561459057600080fd5b8151611d81816138ad56fed681f9d3e640a2dd835404271506ef93f020e2fc065878793505e5ea088fde3dbbbff6eb43d00812703825948233d51219dc930ada33999d17cf576c509bebe56c8f9259db4f6802ea7a1e0a01ddb54668b622f1e8d6b610ad7ba4d95f59da293803482dd7707d12238e38a3b1b5e55fa6e13d81c36ce29ec5c267cc02c53fe317b1774c0811229612ec3762023ccd209d6a131e52cdd22f3427eaa8005bcb2f725bc5812d869f51ca713008babaeead3e54db7feab7d4cb185136396950f0e331a3fa329157566a07927d0c2ba92ff801e4db8af2ec73f92eaf3e7f78d587a84f74125ce1aafb5d1699fc2e5e8f96929ff1a99170dc9bda82c8944acc5c7286b24a0f21470b6927dcbaaf5b1f54865bd687f4a2ce4c43edf1e20339a4c05baece6dbdcc28927f6ed428550e539c70c9145bd20fc6e3d7611bd20e170e9b1840357e26a850dc4edaa8b82b6511eec141075372c9c551d3ddb37c35a301f000188472de2bbf04bc62a7ee894bd625126d381bf5e8b726e5cd498c3a9dad76d85ba26469706673582212209059cc9e8f7e3d93db0d54557f2ea823be7fefe01fcbc046156e35b108fa831c64736f6c63430008110033

Deployed Bytecode

0x6080604052600436106101f95760003560e01c806360da3e831161010d578063a81d04e7116100a0578063e3ca2d651161006f578063e3ca2d65146105a2578063ef5ca789146105c2578063f851a440146105e5578063fcee45f4146105fa578063fdc3a3d51461063457600080fd5b8063a81d04e714610506578063b584686914610533578063b7ba18c714610553578063c5c51dca1461058057600080fd5b806375829def116100dc57806375829def14610478578063854e0c6f1461049857806395d89b41146104b85780639b21cf6c146104cd57600080fd5b806360da3e83146103f357806370a0823114610418578063721c651314610438578063738b62e51461045857600080fd5b80632c4e722e116101905780633af9e6691161015f5780633af9e6691461035e5780633e25e8371461037e5780634538e0091461039357806349d6ed56146103b35780634da05606146103d357600080fd5b80632c4e722e14610305578063313ce5671461031a57806332a9caba146103365780633a4b66f11461035657600080fd5b80631468f9ef116101cc5780631468f9ef1461028357806318160ddd146102a3578063235b64c2146102b857806326782247146102d857600080fd5b806306fdde03146101fe5780630e18b681146102295780630eb6f8d914610240578063143a08d414610260575b600080fd5b34801561020a57600080fd5b50610213610654565b604051610220919061389a565b60405180910390f35b34801561023557600080fd5b5061023e6106a3565b005b34801561024c57600080fd5b5061023e61025b366004613906565b610712565b34801561026c57600080fd5b50610275610772565b604051908152602001610220565b34801561028f57600080fd5b5061023e61029e366004613961565b610781565b3480156102af57600080fd5b506102756107b6565b3480156102c457600080fd5b5061023e6102d33660046139c0565b6107c0565b3480156102e457600080fd5b506102ed6107f5565b6040516001600160a01b039091168152602001610220565b34801561031157600080fd5b506102756107ff565b34801561032657600080fd5b5060405160128152602001610220565b34801561034257600080fd5b5061023e6103513660046139ee565b610842565b61023e610879565b34801561036a57600080fd5b50610275610379366004613a1a565b61088b565b34801561038a57600080fd5b5061023e61089c565b34801561039f57600080fd5b5061023e6103ae366004613a37565b610ae4565b3480156103bf57600080fd5b506102756103ce3660046139c0565b610ea5565b3480156103df57600080fd5b5061023e6103ee3660046139c0565b610eb0565b3480156103ff57600080fd5b50610408610ee2565b6040519015158152602001610220565b34801561042457600080fd5b50610275610433366004613a1a565b610f0c565b34801561044457600080fd5b5061023e6104533660046139c0565b610f37565b34801561046457600080fd5b5061023e610473366004613a72565b610f40565b34801561048457600080fd5b5061023e610493366004613a1a565b610fc7565b3480156104a457600080fd5b5061023e6104b3366004613a8f565b610ff9565b3480156104c457600080fd5b5061021361102c565b3480156104d957600080fd5b506104ed6104e8366004613b16565b611057565b6040516001600160e01b03199091168152602001610220565b34801561051257600080fd5b50610526610521366004613961565b611197565b6040516102209190613bd9565b34801561053f57600080fd5b5061040861054e3660046139c0565b61130b565b34801561055f57600080fd5b5061057361056e366004613c66565b611339565b6040516102209190613cff565b34801561058c57600080fd5b50610595611505565b6040516102209190613dea565b3480156105ae57600080fd5b5061023e6105bd366004613dfd565b6115d1565b3480156105ce57600080fd5b506105d7611699565b604051610220929190613e4f565b3480156105f157600080fd5b506102ed61176c565b34801561060657600080fd5b506102756106153660046139c0565b600090815260008051602061463c833981519152602052604090205490565b34801561064057600080fd5b506102ed61064f3660046139c0565b611776565b606061067f7feee152275d096301850a53ae85c6991c818bc6bac8a2174c268aa94ed7cf06f16117cb565b60405160200161068f9190613e7d565b604051602081830303815290604052905090565b6106ab611863565b6001600160a01b0316336001600160a01b0316146106fd57336106cc611863565b60405163295a81c160e01b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b6107063361188d565b61071060006118fb565b565b61071a611960565b6001600160a01b0316336001600160a01b03161461073b57336106cc611960565b61074683151561198a565b600085815260008051602061461c833981519152602052604090205561076c8282611994565b50505050565b600061077c611b46565b905090565b610789611960565b6001600160a01b0316336001600160a01b0316146107aa57336106cc611960565b61076c84848484611b7d565b600061077c611d30565b6107c8611960565b6001600160a01b0316336001600160a01b0316146107e957336106cc611960565b6107f281611d48565b50565b600061077c611863565b60008061080a611d30565b90506000811161082257670de0b6b3a764000061083c565b61083c61082d611b46565b670de0b6b3a764000083611d71565b91505090565b61084a611960565b6001600160a01b0316336001600160a01b03161461086b57336106cc611960565b6108758282611d88565b5050565b61088234611f2a565b6107f234611f4b565b6000610896826125dc565b92915050565b60026108b46000805160206146fc8339815191525490565b036108d25760405163558a1e0360e11b815260040160405180910390fd5b60026000805160206146fc8339815191525547600060008051602061459c83398151915280548060200260200160405190810160405280929190818152602001828054801561094a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161092c575b50505050509050600061096b60008051602061465c83398151915260001b90565b8054806020026020016040519081016040528092919081815260200182805480156109b557602002820191906000526020600020905b8154815260200190600101908083116109a1575b5050505050905060005b8251811015610ac85760006109f0858484815181106109e0576109e0613e99565b6020026020010151612710611d71565b90506000848381518110610a0657610a06613e99565b60200260200101519050806001600160a01b03167fd244b5a3b2e3977ecffe1a5e5ab7661aadfecbae24be711b7a72bb42bd1b2db083604051610a4b91815260200190565b60405180910390a2600080826001600160a01b03168460405160006040518083038185875af1925050503d8060008114610aa1576040519150601f19603f3d011682016040523d82523d6000602084013e610aa6565b606091505b509150915081610ab857805181602001fd5b5050600190920191506109bf9050565b505060016000805160206146fc83398151915255506107109050565b6000610b0e7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b8103610e5a57610b46610b22826001613ec5565b7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7655565b7f91efa3d50feccde0d0d202f8ae5c41ca0b2be614cebcb2bd2f4b019396e6568a81600036604051610b7a93929190613f01565b60405180910390a1610bec610b8f8380613f1b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152507feee152275d096301850a53ae85c6991c818bc6bac8a2174c268aa94ed7cf06f1939250506126489050565b7f4df9dcd34ae35f40f2c756fd8ac83210ed0b76d065543ee73d868aec7c7fcf02610c178380613f1b565b604051610c25929190613f61565b60405180910390a1610c9a610c3d6020840184613f1b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152507f4a8b3e24ebc795477af927068865c6fcc26e359a994edca2492e515a46aad711939250506126489050565b7fadf3ae8bd543b3007d464f15cb8ea1db3f44e84d41d203164f40b95e27558ac6610cc86020840184613f1b565b604051610cd6929190613f61565b60405180910390a1610cf6610cf16060840160408501613a1a565b61188d565b610d036060830183613f75565b9050600003610d255760405163e613e6d960e01b815260040160405180910390fd5b610d326080830183613f75565b9050610d416060840184613f75565b905014610d8757610d556060830183613f75565b9050610d646080840184613f75565b604051635020e50560e01b815260048101939093526024830152506044016106f4565b60005b610d976060840184613f75565b9050811015610e0657610dfe610db06060850185613f75565b83818110610dc057610dc0613e99565b9050602002016020810190610dd59190613a1a565b610de26080860186613f75565b84818110610df257610df2613e99565b90506020020135611d88565b600101610d8a565b50610e1c610e1760e0840184613f75565b611994565b610e3e610e2c60a0840184613f75565b610e3960c0860186613f75565b612654565b610e4c826101000135612677565b610875826101200135611d48565b80610e837fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b60405163031b997760e51b8152600481019290925260248201526044016106f4565b6000610896826126de565b610eb8611960565b6001600160a01b0316336001600160a01b031614610ed957336106cc611960565b6107f281612725565b600061077c7fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e75490565b6001600160a01b038116600090815260008051602061467c8339815191526020526040812054610896565b6107f281612a3f565b610f48611960565b6001600160a01b0316336001600160a01b031614610f6957336106cc611960565b60405181151581527f0797bb255611d0d3d24d654500ba8d9fbb79fd62d6d636a3aafa8bf3d5d6c6709060200160405180910390a16107f27fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e7829055565b610fcf611960565b6001600160a01b0316336001600160a01b031614610ff057336106cc611960565b6107f2816118fb565b611001611960565b6001600160a01b0316336001600160a01b03161461102257336106cc611960565b6108758282611994565b606061067f7f4a8b3e24ebc795477af927068865c6fcc26e359a994edca2492e515a46aad7116117cb565b60008061106333612e15565b600081815260008051602061461c83398151915260205260409020549091506001908116146110a85760405163ca5d071f60e01b8152600481018290526024016106f4565b6001600160a01b0386166110d86000805160206145bc8339815191525b6000848152602091909152604090205490565b6001600160a01b03161415806110f657506001600160a01b03851615155b156111145760405163aea5cf9160e01b815260040160405180910390fd5b60008181526000805160206146dc83398151915260205260408120805486929061113f908490613ec5565b909155505060408051338152602081018390529081018590527ff5833c90fcf127b08719749581fc368b15b6e3e8fd79b1dbc2fb14b7ca6e0ccc9060600160405180910390a1506326c873db60e21b95945050505050565b60608382146111c357604051635020e50560e01b815260048101859052602481018390526044016106f4565b836001600160401b038111156111db576111db613ad0565b60405190808252806020026020018201604052801561120e57816020015b60608152602001906001900390816111f95790505b50905060005b8481101561130257600086868381811061123057611230613e99565b90506020020160208101906112459190613a1a565b9050806001600160a01b031663f8c2153586868581811061126857611268613e99565b905060200281019061127a9190613f75565b6040518363ffffffff1660e01b8152600401611297929190613ff0565b600060405180830381865afa1580156112b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112dc9190810190614027565b8383815181106112ee576112ee613e99565b602090810291909101015250600101611214565b50949350505050565b600061089660008051602061461c8339815191525b6000848152602091909152604090205460019081161490565b606085841461136557604051635020e50560e01b815260048101879052602481018590526044016106f4565b85821461138f57604051635020e50560e01b815260048101879052602481018390526044016106f4565b856001600160401b038111156113a7576113a7613ad0565b6040519080825280602002602001820160405280156113da57816020015b60608152602001906001900390816113c55790505b50905060005b868110156114fa5760008888838181106113fc576113fc613e99565b90506020020160208101906114119190613a1a565b9050806001600160a01b031663adcf116388888581811061143457611434613e99565b90506020028101906114469190613f75565b88888781811061145857611458613e99565b905060200281019061146a9190613f75565b61ffff6040518663ffffffff1660e01b815260040161148d9594939291906140cc565b6000604051808303816000875af11580156114ac573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114d49190810190614142565b8383815181106114e6576114e6613e99565b6020908102919091010152506001016113e0565b509695505050505050565b6060600061151f6000805160206146bc8339815191525490565b90506000816001600160401b0381111561153b5761153b613ad0565b604051908082528060200260200182016040528015611564578160200160208202803683370190505b50905060005b828110156115ca576115986000805160206145bc8339815191525b6000838152602091909152604090205490565b8282815181106115aa576115aa613e99565b6001600160a01b039092166020928302919091019091015260010161156a565b5092915050565b6115d9611960565b6001600160a01b0316336001600160a01b0316146115fa57336106cc611960565b600061160583612e92565b90506116118284612f64565b600061161c84612e92565b60008581526000805160206145dc83398151915260205260408120549192506116458383613ec5565b9050838110156116685760405163cdd14d1b60e01b815260040160405180910390fd5b61167284826141d5565b6000805160206145dc83398151915260009788526020526040909620959095555050505050565b60608060008051602061459c83398151915260008051602061465c83398151915281546040805160208084028201810190925282815291849183018282801561170b57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116116ed575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561175d57602002820191906000526020600020905b815481526020019060010190808311611749575b50505050509050915091509091565b600061077c611960565b600061178e6000805160206146bc8339815191525490565b82106117b05760405163d531737d60e01b8152600481018390526024016106f4565b6108966000805160206145bc8339815191526110c5565b9055565b8054606090829081906117dd906141e8565b80601f0160208091040260200160405190810160405280929190818152602001828054611809906141e8565b80156118565780601f1061182b57610100808354040283529160200191611856565b820191906000526020600020905b81548152906001019060200180831161183957829003601f168201915b5050505050915050919050565b600061077c7f3c1eebcc225c6cc7f5f8765767af6eff617b4139dc3624923a2db67dbca7b68e5490565b61189681613028565b6040516001600160a01b03821681527f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a19060200160405180910390a16107f27f927a17e5ea75d9461748062a2652f4d3698a628896c9832f8488fa0d2846af09829055565b6040516001600160a01b03821681527f2a0f8515de3fa34ef68b99300347b8793c01683350743e96fe440594528298f49060200160405180910390a16107f27f3c1eebcc225c6cc7f5f8765767af6eff617b4139dc3624923a2db67dbca7b68e829055565b600061077c7f927a17e5ea75d9461748062a2652f4d3698a628896c9832f8488fa0d2846af095490565b6000811515610896565b6000805160206146bc8339815191525481146119e357806119c16000805160206146bc8339815191525490565b604051635020e50560e01b8152600481019290925260248201526044016106f4565b60006119fc6000805160206145fc83398151915261304f565b6000805160206145fc83398151915260005b83811015611ae4576000611a2f60008051602061461c833981519152611320565b90506000868684818110611a4557611a45613e99565b90506020020135905081158015611a5b57508015155b15611a7c576040516303b1bb5d60e31b8152600481018490526024016106f4565b868684818110611a8e57611a8e613e99565b9050602002013585611aa09190613ec5565b945083878785818110611ab557611ab5613e99565b835460018101855560009485526020948590209490910292909201359290910191909155505050600101611a0e565b506127108214611b0757604051630a68b9d760e41b815260040160405180910390fd5b7fb73c61a2aeb51508c0c5d9bab21439a39e825916565b3210eddb7310a64e874e8484604051611b38929190613ff0565b60405180910390a150505050565b600080805b6000805160206146bc83398151915254811015611b7757611b6b81613081565b90910190600101611b4b565b50919050565b828114611ba757604051635020e50560e01b815260048101849052602481018290526044016106f4565b611bbe60008051602061465c83398151915261304f565b611bd560008051602061459c83398151915261304f565b6000805b84811015611cc9576000848483818110611bf557611bf5613e99565b9050602002013590508083611c0a9190613ec5565b60008051602061465c83398151915280546001810182556000919091527f7f4138ba837fd9f0217ae6ee377e4094fe6493a1ee263ca7f57cde5da5a7977b01829055925060008051602061459c833981519152878784818110611c6f57611c6f613e99565b9050602002016020810190611c849190613a1a565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b039092169190911790555080611cc18161421c565b915050611bd9565b506127108114611cec57604051630a68b9d760e41b815260040160405180910390fd5b7fbf29864d443bca1c8bf52ec7006503a5e58e06535e2eea7bc37651f7127e376185858585604051611d219493929190614235565b60405180910390a15050505050565b600061077c60008051602061469c8339815191525490565b6107f27f900053b761278bb5de4eeaea5ed9000b89943edad45dcf64a9dab96d0ce29c2e829055565b6000611d7e84848461309f565b90505b9392505050565b611d918161316c565b611d9a82613028565b6000611db26000805160206146bc8339815191525490565b905060005b81811015611e1a57611dd66000805160206145bc833981519152611585565b6001600160a01b0316846001600160a01b031603611e12576040516301b6ee3960e71b81526001600160a01b03851660048201526024016106f4565b600101611db7565b50611e2d836001600160a01b031661318f565b60008281526000805160206145bc833981519152602090815260408083209390935560008051602061463c833981519152905220829055611e6e600161198a565b600082815260008051602061461c8339815191526020526040902055611eaa611e98826001613ec5565b6000805160206146bc83398151915255565b604080516001600160a01b0385168152602081018390527f0c98febfffcec480c66a977e13f14bafdb5199ea9603591a0715b0cabe0c3ae2910160405180910390a160408051828152602081018490527f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5910160405180910390a1505050565b806000036107f25760405163095e705160e11b815260040160405180910390fd5b6000611f757fa030c45ae387079bc9a34aa1365121b47b8ef2d06c04682ce63b90b7c06843e75490565b15611f935760405163deeb694360e01b815260040160405180910390fd5b60006000805160206145fc833981519152805480602002602001604051908101604052809291908181526020018280548015611fee57602002820191906000526020600020905b815481526020019060010190808311611fda575b50505050509050600081516001600160401b0381111561201057612010613ad0565b60405190808252806020026020018201604052801561206957816020015b6040805160a08101825260008082526020808301829052928201819052606082018190526080820152825260001990920191018161202e5790505b5090506000805b6000805160206146bc8339815191525481101561258b57600084828151811061209b5761209b613e99565b6020026020010151111561258357808382815181106120bc576120bc613e99565b6020026020010151600001906001600160801b031690816001600160801b03168152505060006120f8878684815181106109e0576109e0613e99565b9050612103826131a0565b600061210e83611776565b9050600061211a611d30565b90506000612126611b46565b9050655af3107a400082101561226d5760008581527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a602052604081208054869290612173908490613ec5565b925050819055506000836001600160a01b031663d0e30db0866040518263ffffffff1660e01b815260040160206040518083038185885af11580156121bc573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906121e19190614298565b90506121ed8188613ec5565b96506121f9338261320e565b8488878151811061220c5761220c613e99565b6020026020010151602001906001600160801b031690816001600160801b0316815250508088878151811061224357612243613e99565b6020026020010151606001906001600160801b031690816001600160801b0316815250505061257e565b6000612278866126de565b90506000633b9aca008211156124025781861886831102821861229b81886141d5565b965060006122e0826122ac8b6132a5565b60008c81526000805160206146dc83398151915260205260409020546122d291906141d5565b6122db8c613081565b611d71565b9050816000805160206145dc83398151915260008b815260200190815260200160002060008282546123129190613ec5565b92505081905550818b8a8151811061232c5761232c613e99565b6020026020010151604001906001600160801b031690816001600160801b031681525050808b8a8151811061236357612363613e99565b6020908102919091018101516001600160801b03909216608090920191909152604080518381529182018b905281018390527ffdb2feef2c30ce94651104212914c3249c15af2822e625dac7712175f6fce88e9060600160405180910390a160006123cf838888611d71565b9050806000036123f25760405163d7eb52ab60e01b815260040160405180910390fd5b6123fc8185613ec5565b93505050505b85156125655760008781527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a602052604081208054889290612445908490613ec5565b925050819055506000856001600160a01b031663d0e30db0886040518263ffffffff1660e01b815260040160206040518083038185885af115801561248e573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906124b39190614298565b905060006124c2888787611d71565b9050806000036124e55760405163d7eb52ab60e01b815260040160405180910390fd5b878b8a815181106124f8576124f8613e99565b602002602001015160200181815161251091906142b1565b6001600160801b03169052508a5182908c908b90811061253257612532613e99565b602002602001015160600181815161254a91906142b1565b6001600160801b03169052506125608184613ec5565b925050505b61256f338261320e565b6125798189613ec5565b975050505b505050505b600101612070565b50336001600160a01b03167f22064e3ba88064d71ff211d550f2b1f3e63b19eb3fd647544e21c8f6e5ccc42e8683856040516125c9939291906142d1565b60405180910390a2506001949350505050565b6000806125e7611b46565b905060006125f3611d30565b9050811580612600575080155b1561260f575060009392505050565b6001600160a01b038416600090815260008051602061467c8339815191526020526040902054612640908383611d71565b949350505050565b818061076c83826143b3565b61266084848484611b7d565b60016000805160206146fc8339815191525561076c565b6126808161316c565b6126a97f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca8785829055565b6040518181527ed422a7205dd59ab69db2ebeb5699ccb6e9e5eed515043c5e4677221986ad3b9060200160405180910390a150565b6000806126ea83612e92565b60008481526000805160206145dc83398151915260205260409020549091508082111561271b5761264081836141d5565b5060009392505050565b6000805160206146bc8339815191525481106127575760405163d531737d60e01b8152600481018290526024016106f4565b6000612762826132a5565b9050806000036127885760405163280d7c2760e11b8152600481018390526024016106f4565b600060008051602061459c8339815191528054806020026020016040519081016040528092919081815260200182805480156127ed57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116127cf575b50505050509050600061280e60008051602061465c83398151915260001b90565b80548060200260200160405190810160405280929190818152602001828054801561285857602002820191906000526020600020905b815481526020019060010190808311612844575b50505050509050600061286a85611776565b905060005b83518110156128c6576000612890868584815181106109e0576109e0613e99565b905080156128bd576128bd8782858886815181106128b0576128b0613e99565b60200260200101516133a6565b5060010161286f565b5061298f84826001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612909573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292d9190614298565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561296b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db9190614298565b60008681527f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b6020526040812080549091906129cc908490613ec5565b909155506129db905085612e92565b60008681526000805160206145dc833981519152602052604090819020919091555185907faf5592e0f02600cde237bef50ea4c3508bd3d392bcfc5683b03c1af1c341794b90612a3090879086908890614472565b60405180910390a25050505050565b6000612a5760008051602061469c8339815191525490565b90506000612a63611b46565b9050612a6f3384613506565b6000612a7c848385611d71565b90506000612a966000805160206146bc8339815191525490565b905080600103612b5b57604080516001808252818301909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081612ab7579050509050612b0660008483600081518110612af957612af9613e99565b60200260200101516135e1565b612b106000613720565b336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038783604051612b4b92919061449d565b60405180910390a2505050505050565b60006000805160206145fc833981519152805480602002602001604051908101604052809291908181526020018280548015612bb657602002820191906000526020600020905b815481526020019060010190808311612ba2575b50507f900053b761278bb5de4eeaea5ed9000b89943edad45dcf64a9dab96d0ce29c2e54939450612be692505050565b831015612d125760008060005b84811015612c56576000612c13888684815181106109e0576109e0613e99565b90506000612c2083613081565b90506000612c2e8383614504565b9050888210158015612c3f57508581135b15612c4b578095508394505b505050600101612bf3565b506000821315612d0f57604080516001808252818301909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081612c77579050509050612cb8828783600081518110612af957612af9613e99565b612cc182613720565b336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038a83604051612cfc92919061449d565b60405180910390a2505050505050505050565b50505b6000826001600160401b03811115612d2c57612d2c613ad0565b604051908082528060200260200182016040528015612d7157816020015b6040805180820190915260008082526020820152815260200190600190039081612d4a5790505b50905060005b83811015612dc8576000612d97868584815181106109e0576109e0613e99565b90508015612db657612db68282858581518110612af957612af9613e99565b612dbf82613720565b50600101612d77565b50336001600160a01b03167f75aa83b91343398bcfa338c4017c29780f24e0178bb796993453746801d80b038883604051612e0492919061449d565b60405180910390a250505050505050565b6000805b6000805160206146bc83398151915254811015612e6d57612e476000805160206145bc833981519152611585565b6001600160a01b0316836001600160a01b031603612e655792915050565b600101612e19565b50604051639823215960e01b81526001600160a01b03831660048201526024016106f4565b600080612e9e83613762565b60008481527f03abd4c14227eca60c6fecceef3797455c352f43ab35128096ea0ac0d9b2170a60209081526040808320547f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b9092529091205491925090612f058184613ec5565b8210612f1657506000949350505050565b600082612f238386613ec5565b612f2d91906141d5565b9050612f5a8160008051602061463c83398151915260008981526020919091526040902054612710611d71565b9695505050505050565b7f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca878554821115612fca577f70be78e680b682a5a3c38e305d79e28594fd0c62048cca29ef1bd1d746ca878554604051627431e360e31b81526004016106f491815260200190565b8160008051602061463c833981519152600083815260209182526040908190209290925581518381529081018490527f032dc6a2d839eb179729a55633fdf1c41a1fc4739394154117005db2b354b9b5910160405180910390a15050565b6001600160a01b0381166107f25760405163f6b2911f60e01b815260040160405180910390fd5b8054801561087557600082558160005260206000205b811561307c57600182039150600082820155613065565b505050565b600061308c826126de565b61309583613762565b61089691906141d5565b60008080600019858709858702925082811083820303915050806000036130d9578382816130cf576130cf614524565b0492505050611d81565b83811061310357604051631dcf306360e21b815260048101829052602481018590526044016106f4565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6127108111156107f257604051630a68b9d760e41b815260040160405180910390fd5b60006001600160a01b038216610896565b6000805160206146bc8339815191525481106131d25760405163d531737d60e01b8152600481018290526024016106f4565b60006131eb60008051602061461c833981519152611320565b9050806108755760405163ca5d071f60e01b8152600481018390526024016106f4565b6132448161322860008051602061469c8339815191525490565b6132329190613ec5565b60008051602061469c83398151915255565b6001600160a01b038216600081815260008051602061467c83398151915260209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60008181526000805160206145bc83398151915260205260408120546000816001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190614298565b9050801561271b576133a1613339856126de565b836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613377573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061339b9190614298565b83611d71565b612640565b60008481526000805160206146dc8339815191526020526040812080548592906133d19084906141d5565b925050819055506000826001600160a01b0316637f9654f5846001600160a01b031663ffed4bf56040518163ffffffff1660e01b8152600401602060405180830381865afa158015613427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061344b919061453a565b6040516bffffffffffffffffffffffff19606087901b16602082015287906034016040516020818303038152906040526040518463ffffffff1660e01b815260040161349993929190614557565b6020604051808303816000875af11580156134b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134dc919061457e565b9050806134ff5760405163a10785df60e01b8152600481018690526024016106f4565b5050505050565b6001600160a01b038216600090815260008051602061467c8339815191526020526040902054808211156135575760405163cf47918160e01b815260048101839052602481018290526044016106f4565b61357b8261357160008051602061469c8339815191525490565b61323291906141d5565b81810360008051602061467c8339815191526001600160a01b03851660008181526020928352604080822094909455925185815290917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60006135ec84611776565b9050600061369484836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613632573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136569190614298565b846001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561296b573d6000803e3d6000fd5b905060006136a186613762565b6001600160801b0380841660208701528716855290506136c3868385336133a6565b6136cc86613762565b6136d690826141d5565b60008781527f76a0ecda094c6ccf2a55f6f1ef41b98d3c1f2dfcb9c1970701fe842ce778ff9b602052604081208054909190613713908490613ec5565b9091555050505050505050565b60008181526000805160206146dc833981519152602052604090205461374a90600a612710611d71565b613753826132a5565b11156107f2576107f281612725565b60008061376e83611776565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d49190614298565b9050806000036137e8575060009392505050565b6126406000805160206146dc833981519152600086815260200190815260200160002054836001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613377573d6000803e3d6000fd5b60005b8381101561386557818101518382015260200161384d565b50506000910152565b6000815180845261388681602086016020860161384a565b601f01601f19169290920160200192915050565b602081526000611d81602083018461386e565b80151581146107f257600080fd5b60008083601f8401126138cd57600080fd5b5081356001600160401b038111156138e457600080fd5b6020830191508360208260051b85010111156138ff57600080fd5b9250929050565b6000806000806060858703121561391c57600080fd5b84359350602085013561392e816138ad565b925060408501356001600160401b0381111561394957600080fd5b613955878288016138bb565b95989497509550505050565b6000806000806040858703121561397757600080fd5b84356001600160401b038082111561398e57600080fd5b61399a888389016138bb565b909650945060208701359150808211156139b357600080fd5b50613955878288016138bb565b6000602082840312156139d257600080fd5b5035919050565b6001600160a01b03811681146107f257600080fd5b60008060408385031215613a0157600080fd5b8235613a0c816139d9565b946020939093013593505050565b600060208284031215613a2c57600080fd5b8135611d81816139d9565b600060208284031215613a4957600080fd5b81356001600160401b03811115613a5f57600080fd5b82016101408185031215611d8157600080fd5b600060208284031215613a8457600080fd5b8135611d81816138ad565b60008060208385031215613aa257600080fd5b82356001600160401b03811115613ab857600080fd5b613ac4858286016138bb565b90969095509350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613b0e57613b0e613ad0565b604052919050565b60008060008060808587031215613b2c57600080fd5b8435613b37816139d9565b9350602085810135613b48816139d9565b93506040860135925060608601356001600160401b0380821115613b6b57600080fd5b818801915088601f830112613b7f57600080fd5b813581811115613b9157613b91613ad0565b613ba3601f8201601f19168501613ae6565b91508082528984828501011115613bb957600080fd5b808484018584013760008482840101525080935050505092959194509250565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b83811015613c5857888603603f19018552825180518088529088019088880190845b81811015613c4257835160070b8352928a0192918a0191600101613c23565b5090975050509386019391860191600101613c01565b509398975050505050505050565b60008060008060008060608789031215613c7f57600080fd5b86356001600160401b0380821115613c9657600080fd5b613ca28a838b016138bb565b90985096506020890135915080821115613cbb57600080fd5b613cc78a838b016138bb565b90965094506040890135915080821115613ce057600080fd5b50613ced89828a016138bb565b979a9699509497509295939492505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613d9957878503603f1901845281518051808752908701908787019060005b81811015613d8357835160038110613d7157634e487b7160e01b600052602160045260246000fd5b83529289019291890191600101613d49565b5090965050509285019290850190600101613d26565b5092979650505050505050565b600081518084526020808501945080840160005b83811015613ddf5781516001600160a01b031687529582019590820190600101613dba565b509495945050505050565b602081526000611d816020830184613da6565b60008060408385031215613e1057600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b83811015613ddf57815187529582019590820190600101613e33565b604081526000613e626040830185613da6565b8281036020840152613e748185613e1f565b95945050505050565b60008251613e8f81846020870161384a565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561089657610896613eaf565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b838152604060208201526000613e74604083018486613ed8565b6000808335601e19843603018112613f3257600080fd5b8301803591506001600160401b03821115613f4c57600080fd5b6020019150368190038213156138ff57600080fd5b602081526000611d7e602083018486613ed8565b6000808335601e19843603018112613f8c57600080fd5b8301803591506001600160401b03821115613fa657600080fd5b6020019150600581901b36038213156138ff57600080fd5b81835260006001600160fb1b03831115613fd757600080fd5b8260051b80836020870137939093016020019392505050565b602081526000611d7e602083018486613fbe565b60006001600160401b0382111561401d5761401d613ad0565b5060051b60200190565b6000602080838503121561403a57600080fd5b82516001600160401b0381111561405057600080fd5b8301601f8101851361406157600080fd5b805161407461406f82614004565b613ae6565b81815260059190911b8201830190838101908783111561409357600080fd5b928401925b828410156140c15783518060070b81146140b25760008081fd5b82529284019290840190614098565b979650505050505050565b6060815260006140e0606083018789613fbe565b828103602084810191909152858252869181016000805b8881101561412657843563ffffffff8116808214614113578384fd5b84525093830193918301916001016140f7565b505080935050505061ffff831660408301529695505050505050565b6000602080838503121561415557600080fd5b82516001600160401b0381111561416b57600080fd5b8301601f8101851361417c57600080fd5b805161418a61406f82614004565b81815260059190911b820183019083810190878311156141a957600080fd5b928401925b828410156140c1578351600381106141c65760008081fd5b825292840192908401906141ae565b8181038181111561089657610896613eaf565b600181811c908216806141fc57607f821691505b602082108103611b7757634e487b7160e01b600052602260045260246000fd5b60006001820161422e5761422e613eaf565b5060010190565b6040808252810184905260008560608301825b8781101561427857823561425b816139d9565b6001600160a01b0316825260209283019290910190600101614248565b50838103602085015261428c818688613fbe565b98975050505050505050565b6000602082840312156142aa57600080fd5b5051919050565b6001600160801b038181168382160190808211156115ca576115ca613eaf565b600060608083016001600160801b03808816855260208188168187015260408481880152838851808652608095508589019150838a0160005b8281101561435357815180518816855286810151881687860152858101518816868601528981015188168a86015288015187168885015260a0909301929085019060010161430a565b50919c9b505050505050505050505050565b601f82111561307c57600081815260208120601f850160051c8101602086101561438c5750805b601f850160051c820191505b818110156143ab57828155600101614398565b505050505050565b81516001600160401b038111156143cc576143cc613ad0565b6143e0816143da84546141e8565b84614365565b602080601f83116001811461441557600084156143fd5750858301515b600019600386901b1c1916600185901b1785556143ab565b600085815260208120601f198616915b8281101561444457888601518255948401946001909101908401614425565b50858210156144625787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b83815260606020820152600061448b6060830185613e1f565b8281036040840152612f5a8185613da6565b600060408083016001600160801b03808716855260208381870152828751808552606088019150828901945060005b818110156144f557855180518616845284015185168484015294830194918601916001016144cc565b50909998505050505050505050565b81810360008312801583831316838312821617156115ca576115ca613eaf565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561454c57600080fd5b8151611d81816139d9565b60018060a01b0384168152826020820152606060408201526000613e74606083018461386e565b60006020828403121561459057600080fd5b8151611d81816138ad56fed681f9d3e640a2dd835404271506ef93f020e2fc065878793505e5ea088fde3dbbbff6eb43d00812703825948233d51219dc930ada33999d17cf576c509bebe56c8f9259db4f6802ea7a1e0a01ddb54668b622f1e8d6b610ad7ba4d95f59da293803482dd7707d12238e38a3b1b5e55fa6e13d81c36ce29ec5c267cc02c53fe317b1774c0811229612ec3762023ccd209d6a131e52cdd22f3427eaa8005bcb2f725bc5812d869f51ca713008babaeead3e54db7feab7d4cb185136396950f0e331a3fa329157566a07927d0c2ba92ff801e4db8af2ec73f92eaf3e7f78d587a84f74125ce1aafb5d1699fc2e5e8f96929ff1a99170dc9bda82c8944acc5c7286b24a0f21470b6927dcbaaf5b1f54865bd687f4a2ce4c43edf1e20339a4c05baece6dbdcc28927f6ed428550e539c70c9145bd20fc6e3d7611bd20e170e9b1840357e26a850dc4edaa8b82b6511eec141075372c9c551d3ddb37c35a301f000188472de2bbf04bc62a7ee894bd625126d381bf5e8b726e5cd498c3a9dad76d85ba26469706673582212209059cc9e8f7e3d93db0d54557f2ea823be7fefe01fcbc046156e35b108fa831c64736f6c63430008110033

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.