ETH Price: $2,632.85 (+2.61%)

Contract

0x04C2e952925B0D742Cf6bcC9a7E41d93fABCA140
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040170872482023-04-20 10:55:35558 days ago1681988135IN
 Create: MultiVaultFacetWithdraw
0 ETH0.2024818860

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MultiVaultFacetWithdraw

Compiler Version
v0.8.0+commit.c7dfd78e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU AGPLv3 license
File 1 of 33 : MultiVaultFacetWithdraw.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;

import "../../interfaces/multivault/IMultiVaultFacetWithdraw.sol";
import "../../interfaces/multivault/IMultiVaultFacetPendingWithdrawals.sol";
import "../../interfaces/multivault/IMultiVaultFacetTokens.sol";
import "../../interfaces/multivault/IMultiVaultFacetFees.sol";
import "../../interfaces/IEverscale.sol";
import "../../interfaces/IERC20.sol";

import "../../libraries/SafeERC20.sol";

import "../helpers/MultiVaultHelperFee.sol";
import "../helpers/MultiVaultHelperReentrancyGuard.sol";
import "../helpers/MultiVaultHelperWithdraw.sol";
import "../helpers/MultiVaultHelperEmergency.sol";
import "../helpers/MultiVaultHelperTokens.sol";
import "../helpers/MultiVaultHelperPendingWithdrawal.sol";
import "../helpers/MultiVaultHelperTokenBalance.sol";
import "../helpers/MultiVaultHelperCallback.sol";


contract MultiVaultFacetWithdraw is
    MultiVaultHelperFee,
    MultiVaultHelperReentrancyGuard,
    MultiVaultHelperWithdraw,
    MultiVaultHelperPendingWithdrawal,
    MultiVaultHelperTokens,
    MultiVaultHelperTokenBalance,
    MultiVaultHelperCallback,
    IMultiVaultFacetWithdraw
{
    using SafeERC20 for IERC20;

    /// @notice Save withdrawal for native token
    /// @param payload Withdraw payload
    /// @param signatures Payload signatures
    function saveWithdrawNative(
        bytes memory payload,
        bytes[] memory signatures
    )
        external
        override
        nonReentrant
        withdrawalNotSeenBefore(payload)
        onlyEmergencyDisabled
    {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IEverscale.EverscaleEvent memory _event = _processWithdrawEvent(
            payload,
            signatures,
            s.configurationNative_
        );

        bytes32 payloadId = keccak256(payload);

        // Decode event data
        NativeWithdrawalParams memory withdrawal = decodeNativeWithdrawalEventData(_event.eventData);

        // Ensure chain id is correct
        require(withdrawal.chainId == block.chainid);

        // Derive token address
        // Depends on the withdrawn token source
        address token = _getNativeWithdrawalToken(withdrawal);

        // Ensure token is not blacklisted
        require(!s.tokens_[token].blacklisted);

        // Consider movement fee and send it to `rewards_`
        uint256 fee = _calculateMovementFee(
            withdrawal.amount,
            token,
            IMultiVaultFacetFees.Fee.Withdraw
        );

        _increaseTokenFee(token, fee);

        _withdraw(
            withdrawal.recipient,
            withdrawal.amount,
            fee,
            IMultiVaultFacetTokens.TokenType.Native,
            payloadId,
            token
        );

        _callbackNativeWithdrawal(withdrawal, withdrawal.amount - fee);
    }

    /// @notice Save withdrawal of alien token
    /// @param signatures List of payload signatures
    /// @param bounty Bounty size
    function saveWithdrawAlien(
        bytes memory payload,
        bytes[] memory signatures,
        uint bounty
    )
        public
        override
        nonReentrant
        withdrawalNotSeenBefore(payload)
        onlyEmergencyDisabled
    {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IEverscale.EverscaleEvent memory _event = _processWithdrawEvent(
            payload,
            signatures,
            s.configurationAlien_
        );

        bytes32 payloadId = keccak256(payload);

        // Decode event data
        AlienWithdrawalParams memory withdrawal = decodeAlienWithdrawalEventData(_event.eventData);

        // Ensure chain id is correct
        require(withdrawal.chainId == block.chainid); // TODO: add errors

        // Ensure token is not blacklisted
        require(!s.tokens_[withdrawal.token].blacklisted);

        // Consider movement fee and send it to `rewards_`
        uint256 fee = _calculateMovementFee(
            withdrawal.amount,
            withdrawal.token,
            IMultiVaultFacetFees.Fee.Withdraw
        );

        _increaseTokenFee(withdrawal.token, fee);

        uint withdrawAmount = withdrawal.amount - fee;

        // Consider withdrawal period limit
        IMultiVaultFacetPendingWithdrawals.WithdrawalPeriodParams memory withdrawalPeriod = _withdrawalPeriod(
            withdrawal.token,
            _event.eventTimestamp
        );

        _withdrawalPeriodIncreaseTotalByTimestamp(
            withdrawal.token,
            _event.eventTimestamp,
            withdrawal.amount
        );

        bool withdrawalLimitsPassed = _withdrawalPeriodCheckLimitsPassed(
            withdrawal.token,
            withdrawal.amount,
            withdrawalPeriod
        );

        // Token balance sufficient and none of the limits are violated
        if (withdrawal.amount <= _vaultTokenBalance(withdrawal.token) && withdrawalLimitsPassed) {

            _withdraw(
                withdrawal.recipient,
                withdrawal.amount,
                fee,
                IMultiVaultFacetTokens.TokenType.Alien,
                payloadId,
                withdrawal.token
            );

            _callbackAlienWithdrawal(withdrawal, withdrawAmount);

            return;
        }

        // Create pending withdrawal
        uint pendingWithdrawalId = s.pendingWithdrawalsPerUser[withdrawal.recipient];

        s.pendingWithdrawalsPerUser[withdrawal.recipient]++;

        s.pendingWithdrawalsTotal[withdrawal.token] += withdrawAmount;

        require(bounty <= withdrawAmount);

        // - Save withdrawal as pending
        s.pendingWithdrawals_[withdrawal.recipient][pendingWithdrawalId] = IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams({
            token: withdrawal.token,
            amount: withdrawAmount,
            bounty: msg.sender == withdrawal.recipient ? bounty : 0,
            timestamp: _event.eventTimestamp,
            approveStatus: IMultiVaultFacetPendingWithdrawals.ApproveStatus.NotRequired,
            chainId: withdrawal.chainId,
            callback: withdrawal.callback
        });

        emit PendingWithdrawalCreated(
            withdrawal.recipient,
            pendingWithdrawalId,
            withdrawal.token,
            withdrawAmount,
            payloadId
        );

        if (!withdrawalLimitsPassed) {
            _pendingWithdrawalApproveStatusUpdate(
                IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId(withdrawal.recipient, pendingWithdrawalId),
                IMultiVaultFacetPendingWithdrawals.ApproveStatus.Required
            );
        }

        _callbackAlienWithdrawalPendingCreated(withdrawal, pendingWithdrawalId);
    }

    /// @notice Save withdrawal of alien token
    function saveWithdrawAlien(
        bytes memory payload,
        bytes[] memory signatures
    )  external override {
        saveWithdrawAlien(payload, signatures, 0);
    }

    function decodeNativeWithdrawalEventData(
        bytes memory eventData
    ) internal pure returns (NativeWithdrawalParams memory) {
        (
            int8 native_wid,
            uint256 native_addr,

            string memory name,
            string memory symbol,
            uint8 decimals,

            uint128 amount,
            uint160 recipient,
            uint256 chainId,

            uint160 callback_recipient,
            bytes memory callback_payload,
            bool callback_strict
        ) = abi.decode(
            eventData,
            (
                int8, uint256,
                string, string, uint8,
                uint128, uint160, uint256,
                uint160, bytes, bool
            )
        );

        return NativeWithdrawalParams({
            native: IEverscale.EverscaleAddress(native_wid, native_addr),
            meta: IMultiVaultFacetTokens.TokenMeta(name, symbol, decimals),
            amount: amount,
            recipient: address(recipient),
            chainId: chainId,
            callback: Callback(
                address(callback_recipient),
                callback_payload,
                callback_strict
            )
        });
    }

    function decodeAlienWithdrawalEventData(
        bytes memory eventData
    ) internal pure returns (AlienWithdrawalParams memory) {
        (
            uint160 token,
            uint128 amount,
            uint160 recipient,
            uint256 chainId,

            uint160 callback_recipient,
            bytes memory callback_payload,
            bool callback_strict
        ) = abi.decode(
            eventData,
            (
                uint160, uint128, uint160, uint256,
                uint160, bytes, bool
            )
        );

        return AlienWithdrawalParams({
            token: address(token),
            amount: uint256(amount),
            recipient: address(recipient),
            chainId: chainId,
            callback: Callback(
                address(callback_recipient),
                callback_payload,
                callback_strict
            )
        });
    }

    function withdrawalIds(bytes32 id) external view override returns(bool) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        return s.withdrawalIds[id];
    }
}

File 2 of 33 : MultiVaultToken.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
pragma solidity 0.8.0;


import "./interfaces/IERC20.sol";
import "./interfaces/IERC20Metadata.sol";
import "./interfaces/IMultiVaultToken.sol";

import "./libraries/Address.sol";

import "./utils/Ownable.sol";


contract MultiVaultToken is IMultiVaultToken, Context, IERC20, IERC20Metadata, Ownable {
    uint activation;

    constructor() Ownable(_msgSender()) {}

    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name}, {symbol} and {decimals}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function initialize(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) external override {
        require(activation == 0);

        _name = name_;
        _symbol = symbol_;
        _decimals = decimals_;

        activation = block.number;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    function mint(
        address account,
        uint amount
    ) external override onlyOwner {
        _mint(account, amount);
    }

    function burn(
        address account,
        uint amount
    ) external override onlyOwner {
        _burn(account, amount);
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
    unchecked {
        _approve(owner, spender, currentAllowance - subtractedValue);
    }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
    unchecked {
        _balances[from] = fromBalance - amount;
    }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
    unchecked {
        _balances[account] = accountBalance - amount;
    }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
        unchecked {
            _approve(owner, spender, currentAllowance - amount);
        }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 3 of 33 : IBridge.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;

import "./IEverscale.sol";
pragma experimental ABIEncoderV2;


interface IBridge is IEverscale {
    struct Round {
        uint32 end;
        uint32 ttl;
        uint32 relays;
        uint32 requiredSignatures;
    }

    function updateMinimumRequiredSignatures(uint32 _minimumRequiredSignatures) external;
    function setConfiguration(EverscaleAddress calldata _roundRelaysConfiguration) external;
    function updateRoundTTL(uint32 _roundTTL) external;

    function isRelay(
        uint32 round,
        address candidate
    ) external view returns (bool);

    function isBanned(
        address candidate
    ) external view returns (bool);

    function isRoundRotten(
        uint32 round
    ) external view returns (bool);

    function verifySignedEverscaleEvent(
        bytes memory payload,
        bytes[] memory signatures
    ) external view returns (uint32);

    function setRoundRelays(
        bytes calldata payload,
        bytes[] calldata signatures
    ) external;

    function forceRoundRelays(
        uint160[] calldata _relays,
        uint32 roundEnd
    ) external;

    function banRelays(
        address[] calldata _relays
    ) external;

    function unbanRelays(
        address[] calldata _relays
    ) external;

    function pause() external;
    function unpause() external;

    function setRoundSubmitter(address _roundSubmitter) external;

    event EmergencyShutdown(bool active);

    event UpdateMinimumRequiredSignatures(uint32 value);
    event UpdateRoundTTL(uint32 value);
    event UpdateRoundRelaysConfiguration(EverscaleAddress configuration);
    event UpdateRoundSubmitter(address _roundSubmitter);

    event NewRound(uint32 indexed round, Round meta);
    event RoundRelay(uint32 indexed round, address indexed relay);
    event BanRelay(address indexed relay, bool status);
}

File 4 of 33 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity 0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 5 of 33 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity 0.8.0;

import "./IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 6 of 33 : IEverscale.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IEverscale {
    struct EverscaleAddress {
        int8 wid;
        uint256 addr;
    }

    struct EverscaleEvent {
        uint64 eventTransactionLt;
        uint32 eventTimestamp;
        bytes eventData;
        int8 configurationWid;
        uint256 configurationAddress;
        int8 eventContractWid;
        uint256 eventContractAddress;
        address proxy;
        uint32 round;
    }
}

File 7 of 33 : IMultiVaultToken.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultToken {
    function initialize(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) external;

    function burn(address account, uint256 amount) external;
    function mint(address account, uint256 amount) external;
}

File 8 of 33 : IMultiVaultFacetFees.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultFacetFees {
    enum Fee { Deposit, Withdraw }

    function defaultNativeDepositFee() external view returns (uint);
    function defaultNativeWithdrawFee() external view returns (uint);
    function defaultAlienDepositFee() external view returns (uint);
    function defaultAlienWithdrawFee() external view returns (uint);

    function fees(address token) external view returns (uint);

    function skim(
        address token
    ) external;

    function setDefaultAlienWithdrawFee(uint fee) external;
    function setDefaultAlienDepositFee(uint fee) external;
    function setDefaultNativeWithdrawFee(uint fee) external;
    function setDefaultNativeDepositFee(uint fee) external;

    function setTokenWithdrawFee(
        address token,
        uint _withdrawFee
    ) external;
    function setTokenDepositFee(
        address token,
        uint _depositFee
    ) external;
}

File 9 of 33 : IMultiVaultFacetFeesEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultFacetFeesEvents {
    event UpdateDefaultNativeDepositFee(uint fee);
    event UpdateDefaultNativeWithdrawFee(uint fee);
    event UpdateDefaultAlienDepositFee(uint fee);
    event UpdateDefaultAlienWithdrawFee(uint fee);

    event UpdateTokenDepositFee(address token, uint256 fee);
    event UpdateTokenWithdrawFee(address token, uint256 fee);

    event EarnTokenFee(address token, uint amount);

    event SkimFee(
        address token,
        uint256 amount
    );
}

File 10 of 33 : IMultiVaultFacetLiquidity.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultFacetLiquidity {
    struct Liquidity {
        uint activation;
        uint supply;
        uint cash;
        uint interest;
    }

    function mint(
        address token,
        uint amount,
        address receiver
    ) external;

    function redeem(
        address token,
        uint amount,
        address receiver
    ) external;

    function exchangeRateCurrent(
        address token
    ) external view returns(uint);

    function getCash(
        address token
    ) external view returns(uint);

    function getSupply(
        address token
    ) external view returns(uint);

    function getLPToken(
        address token
    ) external view returns (address);

    function setTokenInterest(
        address token,
        uint interest
    ) external;

    function setDefaultInterest(
        uint interest
    ) external;

    function liquidity(
        address token
    ) external view returns (Liquidity memory);

    function convertLPToUnderlying(
        address token,
        uint amount
    ) external view returns (uint);

    function convertUnderlyingToLP(
        address token,
        uint amount
    ) external view returns (uint);
}

File 11 of 33 : IMultiVaultFacetLiquidityEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultFacetLiquidityEvents {
    event UpdateTokenLiquidityInterest(address token, uint interest);
    event UpdateDefaultLiquidityInterest(uint inetrest);

    event MintLiquidity(address sender, address token, uint amount, uint lp_amount);
    event RedeemLiquidity(address sender, address token, uint amount, uint underlying_amount);

    event EarnTokenCash(address token, uint amount);
}

File 12 of 33 : IMultiVaultFacetPendingWithdrawals.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../IEverscale.sol";
import "./IMultiVaultFacetWithdraw.sol";


interface IMultiVaultFacetPendingWithdrawals {
    enum ApproveStatus { NotRequired, Required, Approved, Rejected }

    struct WithdrawalLimits {
        uint undeclared;
        uint daily;
        bool enabled;
    }

    struct PendingWithdrawalParams {
        address token;
        uint256 amount;
        uint256 bounty;
        uint256 timestamp;
        ApproveStatus approveStatus;

        uint256 chainId;
        IMultiVaultFacetWithdraw.Callback callback;
    }

    struct PendingWithdrawalId {
        address recipient;
        uint256 id;
    }

    struct WithdrawalPeriodParams {
        uint256 total;
        uint256 considered;
    }

    function pendingWithdrawalsPerUser(address user) external view returns (uint);
    function pendingWithdrawalsTotal(address token) external view returns (uint);

    function pendingWithdrawals(
        address user,
        uint256 id
    ) external view returns (PendingWithdrawalParams memory);

    function setPendingWithdrawalBounty(
        uint256 id,
        uint256 bounty
    ) external;

    function cancelPendingWithdrawal(
        uint256 id,
        uint256 amount,
        IEverscale.EverscaleAddress memory recipient,
        uint expected_evers,
        bytes memory payload,
        uint bounty
    ) external payable;

    function setPendingWithdrawalApprove(
        PendingWithdrawalId memory pendingWithdrawalId,
        ApproveStatus approveStatus
    ) external;

    function setPendingWithdrawalApprove(
        PendingWithdrawalId[] memory pendingWithdrawalId,
        ApproveStatus[] memory approveStatus
    ) external;

    function forceWithdraw(
        PendingWithdrawalId[] memory pendingWithdrawalIds
    ) external;

    function withdrawalLimits(
        address token
    ) external view returns(WithdrawalLimits memory);

    function withdrawalPeriods(
        address token,
        uint256 withdrawalPeriodId
    ) external view returns (WithdrawalPeriodParams memory);
}

File 13 of 33 : IMultiVaultFacetPendingWithdrawalsEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "./IMultiVaultFacetPendingWithdrawals.sol";


interface IMultiVaultFacetPendingWithdrawalsEvents {
    event PendingWithdrawalCancel(
        address recipient,
        uint256 id,
        uint256 amount
    );

    event PendingWithdrawalUpdateBounty(
        address recipient,
        uint256 id,
        uint256 bounty
    );

    event PendingWithdrawalCreated(
        address recipient,
        uint256 id,
        address token,
        uint256 amount,
        bytes32 payloadId
    );

    event PendingWithdrawalWithdraw(
        address recipient,
        uint256 id,
        uint256 amount
    );

    event PendingWithdrawalFill(
        address recipient,
        uint256 id
    );

    event PendingWithdrawalForce(
        address recipient,
        uint256 id
    );

    event PendingWithdrawalUpdateApproveStatus(
        address recipient,
        uint256 id,
        IMultiVaultFacetPendingWithdrawals.ApproveStatus approveStatus
    );
}

File 14 of 33 : IMultiVaultFacetTokens.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "./../IEverscale.sol";


interface IMultiVaultFacetTokens {
    enum TokenType { Native, Alien }

    struct TokenPrefix {
        uint activation;
        string name;
        string symbol;
    }

    struct TokenMeta {
        string name;
        string symbol;
        uint8 decimals;
    }

    struct Token {
        uint activation;
        bool blacklisted;
        uint depositFee;
        uint withdrawFee;
        bool isNative;
        address custom;
    }

    function prefixes(address _token) external view returns (TokenPrefix memory);
    function tokens(address _token) external view returns (Token memory);
    function natives(address _token) external view returns (IEverscale.EverscaleAddress memory);

    function setPrefix(
        address token,
        string memory name_prefix,
        string memory symbol_prefix
    ) external;

    function setTokenBlacklist(
        address token,
        bool blacklisted
    ) external;

    function getNativeToken(
        IEverscale.EverscaleAddress memory native
    ) external view returns (address);
}

File 15 of 33 : IMultiVaultFacetTokensEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


interface IMultiVaultFacetTokensEvents {
    event BlacklistTokenAdded(address token);
    event BlacklistTokenRemoved(address token);

    event TokenActivated(
        address token,
        uint activation,
        bool isNative,
        uint depositFee,
        uint withdrawFee
    );

    event TokenCreated(
        address token,
        int8 native_wid,
        uint256 native_addr,
        string name_prefix,
        string symbol_prefix,
        string name,
        string symbol,
        uint8 decimals
    );
}

File 16 of 33 : IMultiVaultFacetWithdraw.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "./IMultiVaultFacetTokens.sol";
import "../IEverscale.sol";


interface IMultiVaultFacetWithdraw {
    struct Callback {
        address recipient;
        bytes payload;
        bool strict;
    }

    struct NativeWithdrawalParams {
        IEverscale.EverscaleAddress native;
        IMultiVaultFacetTokens.TokenMeta meta;
        uint256 amount;
        address recipient;
        uint256 chainId;
        Callback callback;
    }

    struct AlienWithdrawalParams {
        address token;
        uint256 amount;
        address recipient;
        uint256 chainId;
        Callback callback;
    }

    function withdrawalIds(bytes32) external view returns (bool);

    function saveWithdrawNative(
        bytes memory payload,
        bytes[] memory signatures
    ) external;

    function saveWithdrawAlien(
        bytes memory payload,
        bytes[] memory signatures
    ) external;

    function saveWithdrawAlien(
        bytes memory payload,
        bytes[] memory signatures,
        uint bounty
    ) external;
}

File 17 of 33 : IMultiVaultFacetWithdrawEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "./IMultiVaultFacetTokens.sol";


interface IMultiVaultFacetWithdrawEvents {
    event Withdraw(
        IMultiVaultFacetTokens.TokenType _type,
        bytes32 payloadId,
        address token,
        address recipient,
        uint256 amount,
        uint256 fee
    );
}

File 18 of 33 : IOctusCallback.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;

import "./IMultiVaultFacetWithdraw.sol";


interface IOctusCallbackNative {
    function onNativeWithdrawal(
        IMultiVaultFacetWithdraw.NativeWithdrawalParams memory payload,
        uint256 withdrawAmount
    ) external;
}

interface IOctusCallbackAlien {
    function onAlienWithdrawal(
        IMultiVaultFacetWithdraw.AlienWithdrawalParams memory payload,
        uint256 withdrawAmount
    ) external;
    function onAlienWithdrawalPendingCreated(
        IMultiVaultFacetWithdraw.AlienWithdrawalParams memory _payload,
        uint pendingWithdrawalId
    ) external;
}

File 19 of 33 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity 0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 20 of 33 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity 0.8.0;

import "./../interfaces/IERC20.sol";
import "./../libraries/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
    unchecked {
        uint256 oldAllowance = token.allowance(address(this), spender);
        require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
        uint256 newAllowance = oldAllowance - value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 21 of 33 : MultiVaultHelperCallback.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/multivault/IMultiVaultFacetWithdraw.sol";
import "../../interfaces/multivault/IOctusCallback.sol";

abstract contract MultiVaultHelperCallback {
    modifier checkCallbackRecipient(address recipient) {
        require(recipient != address(this));

        if (recipient != address(0)) {
            _;
        }
    }

    function _callbackNativeWithdrawal(
        IMultiVaultFacetWithdraw.NativeWithdrawalParams memory withdrawal,
        uint256 _withdrawAmount
    ) internal checkCallbackRecipient(withdrawal.callback.recipient) {
        bytes memory data = abi.encodeWithSelector(
            IOctusCallbackNative.onNativeWithdrawal.selector,
            withdrawal,
            _withdrawAmount
        );

        _execute(
            withdrawal.callback.recipient,
            data,
            withdrawal.callback.strict
        );
    }

    function _callbackAlienWithdrawal(
        IMultiVaultFacetWithdraw.AlienWithdrawalParams memory _withdrawal,
        uint256 _withdrawAmount
    ) internal checkCallbackRecipient(_withdrawal.callback.recipient) {
        bytes memory data = abi.encodeWithSelector(
            IOctusCallbackAlien.onAlienWithdrawal.selector,
            _withdrawal,
            _withdrawAmount
        );

        _execute(
            _withdrawal.callback.recipient,
            data,
            _withdrawal.callback.strict
        );
    }

    function _callbackAlienWithdrawalPendingCreated(
        IMultiVaultFacetWithdraw.AlienWithdrawalParams memory _withdrawal,
        uint _pendingWithdrawalId
    ) checkCallbackRecipient(_withdrawal.callback.recipient) internal {
        bytes memory data = abi.encodeWithSelector(
            IOctusCallbackAlien.onAlienWithdrawalPendingCreated.selector,
            _withdrawal,
            _pendingWithdrawalId
        );

        _execute(
            _withdrawal.callback.recipient,
            data,
            _withdrawal.callback.strict
        );
    }

    function _execute(
        address recipient,
        bytes memory data,
        bool strict
    ) internal {
        (bool success, ) = recipient.call(data);

        if (strict) require(success);
    }
}

File 22 of 33 : MultiVaultHelperEmergency.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;

import "../storage/MultiVaultStorage.sol";


abstract contract MultiVaultHelperEmergency {
    modifier onlyEmergencyDisabled() {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        require(!s.emergencyShutdown);

        _;
    }
}

File 23 of 33 : MultiVaultHelperFee.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/multivault/IMultiVaultFacetFees.sol";
import "../../interfaces/multivault/IMultiVaultFacetTokens.sol";
import "../../interfaces/multivault/IMultiVaultFacetLiquidity.sol";
import "../../interfaces/multivault/IMultiVaultFacetFeesEvents.sol";

import "../storage/MultiVaultStorage.sol";
import "./MultiVaultHelperLiquidity.sol";


abstract contract MultiVaultHelperFee is MultiVaultHelperLiquidity, IMultiVaultFacetFeesEvents {
    modifier respectFeeLimit(uint fee) {
        require(fee <= MultiVaultStorage.FEE_LIMIT);

        _;
    }

    /// @notice Calculates fee for deposit or withdrawal.
    /// @param amount Amount of tokens.
    /// @param _token Token address.
    /// @param fee Fee type (Deposit = 0, Withdraw = 1).
    function _calculateMovementFee(
        uint256 amount,
        address _token,
        IMultiVaultFacetFees.Fee fee
    ) internal view returns (uint256) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetTokens.Token memory token = s.tokens_[_token];

        uint tokenFee = fee == IMultiVaultFacetFees.Fee.Deposit ? token.depositFee : token.withdrawFee;

        return tokenFee * amount / MultiVaultStorage.MAX_BPS;
    }

    function _increaseTokenFee(
        address token,
        uint _amount
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        if (_amount == 0) return;

        IMultiVaultFacetLiquidity.Liquidity memory liquidity = s.liquidity[token];

        uint amount;

        if (s.liquidity[token].activation == 0) {
            amount = _amount;
        } else {
            uint liquidity_fee = _amount * liquidity.interest / MultiVaultStorage.MAX_BPS;

            amount = _amount - liquidity_fee;

            _increaseTokenCash(token, liquidity_fee);
        }

        if (amount == 0) return;

        s.fees[token] += amount;
        emit EarnTokenFee(token, amount);
    }
}

File 24 of 33 : MultiVaultHelperLiquidity.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/multivault/IMultiVaultFacetLiquidityEvents.sol";
import "../../interfaces/multivault/IMultiVaultFacetLiquidity.sol";
import "../../interfaces/IMultiVaultToken.sol";
import "../storage/MultiVaultStorage.sol";

import "../../MultiVaultToken.sol";


abstract contract MultiVaultHelperLiquidity is IMultiVaultFacetLiquidityEvents {
    modifier onlyActivatedLP(address token) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        require(s.liquidity[token].activation != 0);

        _;
    }

    function _getLPToken(
        address token
    ) internal view returns (address lp) {
        lp = address(uint160(uint(keccak256(abi.encodePacked(
            hex'ff',
            address(this),
            keccak256(abi.encodePacked('LP', token)),
            hex'192c19818bebb5c6c95f5dcb3c3257379fc46fb654780cb06f3211ee77e1a360' // MultiVaultToken init code hash
        )))));
    }

    function _exchangeRateCurrent(
        address token
    ) internal view returns(uint) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetLiquidity.Liquidity memory liquidity = s.liquidity[token];

        if (liquidity.supply == 0 || liquidity.activation == 0) return MultiVaultStorage.LP_EXCHANGE_RATE_BPS;

        return MultiVaultStorage.LP_EXCHANGE_RATE_BPS * liquidity.cash / liquidity.supply;
    }

    function _getCash(
        address token
    ) internal view returns(uint) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetLiquidity.Liquidity memory liquidity = s.liquidity[token];

        return liquidity.cash;
    }

    function _getSupply(
        address token
    ) internal view returns(uint) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetLiquidity.Liquidity memory liquidity = s.liquidity[token];

        return liquidity.supply;
    }

    function _convertLPToUnderlying(
        address token,
        uint amount
    ) internal view returns (uint) {
        return _exchangeRateCurrent(token) * amount / MultiVaultStorage.LP_EXCHANGE_RATE_BPS;
    }

    function _convertUnderlyingToLP(
        address token,
        uint amount
    ) internal view returns (uint) {
        return MultiVaultStorage.LP_EXCHANGE_RATE_BPS * amount / _exchangeRateCurrent(token);
    }

    function _deployLPToken(
        address token
    ) internal returns (address lp) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        require(s.liquidity[token].activation == 0);

        s.liquidity[token].activation = block.number;
        s.liquidity[token].interest = s.defaultInterest;

        bytes memory bytecode = type(MultiVaultToken).creationCode;

        bytes32 salt = keccak256(abi.encodePacked('LP', token));

        assembly {
            lp := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }

        string memory name = IERC20Metadata(token).name();
        string memory symbol = IERC20Metadata(token).symbol();
        uint8 decimals = IERC20Metadata(token).decimals();

        IMultiVaultToken(lp).initialize(
            string(abi.encodePacked(MultiVaultStorage.DEFAULT_NAME_LP_PREFIX, name)),
            string(abi.encodePacked(MultiVaultStorage.DEFAULT_SYMBOL_LP_PREFIX, symbol)),
            decimals
        );
    }

    function _increaseTokenCash(
        address token,
        uint amount
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        if (amount == 0) return;

        s.liquidity[token].cash += amount;

        emit EarnTokenCash(token, amount);
    }
}

File 25 of 33 : MultiVaultHelperPendingWithdrawal.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/multivault/IMultiVaultFacetPendingWithdrawals.sol";
import "../../interfaces/multivault/IMultiVaultFacetPendingWithdrawalsEvents.sol";

import "../storage/MultiVaultStorage.sol";


abstract contract MultiVaultHelperPendingWithdrawal is IMultiVaultFacetPendingWithdrawalsEvents {
    modifier pendingWithdrawalOpened(
        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId memory pendingWithdrawalId
    ) {
        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams memory pendingWithdrawal = _pendingWithdrawal(pendingWithdrawalId);

        require(pendingWithdrawal.amount > 0);

        _;
    }

    function _pendingWithdrawal(
        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId memory pendingWithdrawalId
    ) internal view returns (IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams memory) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        return s.pendingWithdrawals_[pendingWithdrawalId.recipient][pendingWithdrawalId.id];
    }

    function _pendingWithdrawal(
        address recipient,
        uint256 id
    ) internal view returns (IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams memory) {
        return _pendingWithdrawal(IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId(recipient, id));
    }

    function _pendingWithdrawalApproveStatusUpdate(
        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId memory pendingWithdrawalId,
        IMultiVaultFacetPendingWithdrawals.ApproveStatus approveStatus
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        s.pendingWithdrawals_[pendingWithdrawalId.recipient][pendingWithdrawalId.id].approveStatus = approveStatus;

        emit PendingWithdrawalUpdateApproveStatus(
            pendingWithdrawalId.recipient,
            pendingWithdrawalId.id,
            approveStatus
        );
    }

    function _pendingWithdrawalAmountReduce(
        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalId memory pendingWithdrawalId,
        uint amount
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams memory pendingWithdrawal = _pendingWithdrawal(pendingWithdrawalId);

        require(pendingWithdrawal.approveStatus == IMultiVaultFacetPendingWithdrawals.ApproveStatus.NotRequired
            || pendingWithdrawal.approveStatus == IMultiVaultFacetPendingWithdrawals.ApproveStatus.Approved);

        s.pendingWithdrawals_[pendingWithdrawalId.recipient][pendingWithdrawalId.id].amount -= amount;
        s.pendingWithdrawalsTotal[pendingWithdrawal.token] -= amount;
    }

    function _withdrawalPeriod(
        address token,
        uint256 timestamp
    ) internal view returns (IMultiVaultFacetPendingWithdrawals.WithdrawalPeriodParams memory) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        return s.withdrawalPeriods_[token][_withdrawalPeriodDeriveId(timestamp)];
    }

    function _withdrawalPeriodDeriveId(
        uint256 timestamp
    ) internal pure returns (uint256) {
        return timestamp / MultiVaultStorage.WITHDRAW_PERIOD_DURATION_IN_SECONDS;
    }

    function _withdrawalPeriodIncreaseTotalByTimestamp(
        address token,
        uint256 timestamp,
        uint256 amount
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        uint withdrawalPeriodId = _withdrawalPeriodDeriveId(timestamp);

        s.withdrawalPeriods_[token][withdrawalPeriodId].total += amount;
    }

    function _withdrawalPeriodCheckLimitsPassed(
        address token,
        uint amount,
        IMultiVaultFacetPendingWithdrawals.WithdrawalPeriodParams memory withdrawalPeriod
    ) internal view returns (bool) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        IMultiVaultFacetPendingWithdrawals.WithdrawalLimits memory withdrawalLimit = s.withdrawalLimits_[token];

        if (!withdrawalLimit.enabled) return true;

        return (amount < withdrawalLimit.undeclared) &&
        (amount + withdrawalPeriod.total - withdrawalPeriod.considered < withdrawalLimit.daily);
    }
}

File 26 of 33 : MultiVaultHelperReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "./../storage/MultiVaultStorageReentrancyGuard.sol";


abstract contract MultiVaultHelperReentrancyGuard {
    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        MultiVaultStorageReentrancyGuard.ReentrancyGuardStorage storage s = MultiVaultStorageReentrancyGuard._storage();

        // On the first call to nonReentrant, _notEntered will be true
        require(s._status != MultiVaultStorageReentrancyGuard._ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        s._status = MultiVaultStorageReentrancyGuard._ENTERED;
    }

    function _nonReentrantAfter() private {
        MultiVaultStorageReentrancyGuard.ReentrancyGuardStorage storage s = MultiVaultStorageReentrancyGuard._storage();

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

File 27 of 33 : MultiVaultHelperTokenBalance.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/IERC20.sol";


abstract contract MultiVaultHelperTokenBalance {
    function _vaultTokenBalance(
        address token
    ) internal view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
    }
}

File 28 of 33 : MultiVaultHelperTokens.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/multivault/IMultiVaultFacetTokens.sol";
import "../../interfaces/multivault/IMultiVaultFacetWithdraw.sol";
import "../../interfaces/multivault/IMultiVaultFacetTokensEvents.sol";
import "../../interfaces/IEverscale.sol";

import "../../MultiVaultToken.sol";
import "../storage/MultiVaultStorage.sol";
import "./MultiVaultHelperEmergency.sol";


abstract contract MultiVaultHelperTokens is
    MultiVaultHelperEmergency,
    IMultiVaultFacetTokensEvents
{
    modifier initializeToken(address _token) {
        _initializeToken(_token);
        _;
    }
    modifier initializeWethToken() {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        _initializeToken(s.weth);
        _;
    }

    function _initializeToken(address _token) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();
        if (s.tokens_[_token].activation == 0) {
            // Non-activated tokens are always aliens, native tokens are activate on the first `saveWithdrawNative`

            require(
                IERC20Metadata(_token).decimals() <= MultiVaultStorage.DECIMALS_LIMIT &&
                bytes(IERC20Metadata(_token).symbol()).length <= MultiVaultStorage.SYMBOL_LENGTH_LIMIT &&
                bytes(IERC20Metadata(_token).name()).length <= MultiVaultStorage.NAME_LENGTH_LIMIT
            );

            _activateToken(_token, false);
        }
    }

    modifier tokenNotBlacklisted(address _token) {
        bool isBlackListed = isTokenNoBlackListed(_token);
        require(!isBlackListed);

        _;
    }
    modifier wethNotBlacklisted() {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();
        bool isBlackListed = isTokenNoBlackListed(s.weth);
        require(!isBlackListed);

        _;
    }
    function isTokenNoBlackListed(address _token) internal view returns (bool) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();
        return s.tokens_[_token].blacklisted;
    }

    function _activateToken(
        address token,
        bool isNative
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        uint depositFee = isNative ? s.defaultNativeDepositFee : s.defaultAlienDepositFee;
        uint withdrawFee = isNative ? s.defaultNativeWithdrawFee : s.defaultAlienWithdrawFee;

        s.tokens_[token] = IMultiVaultFacetTokens.Token({
            activation: block.number,
            blacklisted: false,
            isNative: isNative,
            depositFee: depositFee,
            withdrawFee: withdrawFee,
            custom: address(0)
        });

        emit TokenActivated(
            token,
            block.number,
            isNative,
            depositFee,
            withdrawFee
        );
    }

    function _getNativeWithdrawalToken(
        IMultiVaultFacetWithdraw.NativeWithdrawalParams memory withdrawal
    ) internal returns (address) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        // Derive native token address from the Everscale (token wid, token addr)
        address token = _getNativeToken(withdrawal.native);

        // Token is being withdrawn first time - activate it (set default parameters)
        // And deploy ERC20 representation
        if (s.tokens_[token].activation == 0) {
            _deployTokenForNative(withdrawal.native, withdrawal.meta);
            _activateToken(token, true);

            s.natives_[token] = withdrawal.native;
        }

        // Check if there is a custom ERC20 representing this withdrawal.native
        address custom = s.tokens_[token].custom;

        if (custom != address(0)) return custom;

        return token;
    }

    function _increaseCash(
        address token,
        uint amount
    ) internal {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        s.liquidity[token].cash += amount;
    }

    /// @notice Gets the address
    /// @param native Everscale token address
    /// @return token Token address
    function _getNativeToken(
        IEverscale.EverscaleAddress memory native
    ) internal view returns (address token) {
        token = address(uint160(uint(keccak256(abi.encodePacked(
            hex'ff',
            address(this),
            keccak256(abi.encodePacked(native.wid, native.addr)),
            hex'192c19818bebb5c6c95f5dcb3c3257379fc46fb654780cb06f3211ee77e1a360' // MultiVaultToken init code hash
        )))));
    }

    function _deployTokenForNative(
        IEverscale.EverscaleAddress memory native,
        IMultiVaultFacetTokens.TokenMeta memory meta
    ) internal returns (address token) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        bytes memory bytecode = type(MultiVaultToken).creationCode;

        bytes32 salt = keccak256(abi.encodePacked(native.wid, native.addr));

        assembly {
            token := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }

        // Check custom prefix available
        IMultiVaultFacetTokens.TokenPrefix memory prefix = s.prefixes_[token];

        string memory name_prefix = prefix.activation == 0 ? MultiVaultStorage.DEFAULT_NAME_PREFIX : prefix.name;
        string memory symbol_prefix = prefix.activation == 0 ? MultiVaultStorage.DEFAULT_SYMBOL_PREFIX : prefix.symbol;

        IMultiVaultToken(token).initialize(
            string(abi.encodePacked(name_prefix, meta.name)),
            string(abi.encodePacked(symbol_prefix, meta.symbol)),
            meta.decimals
        );

        emit TokenCreated(
            token,
            native.wid,
            native.addr,
            name_prefix,
            symbol_prefix,
            meta.name,
            meta.symbol,
            meta.decimals
        );
    }
}

File 29 of 33 : MultiVaultHelperWithdraw.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/IEverscale.sol";
import "../../interfaces/IERC20.sol";
import "../../interfaces/IBridge.sol";
import "../../interfaces/IMultiVaultToken.sol";
import "../../interfaces/multivault/IMultiVaultFacetTokens.sol";
import "../../interfaces/multivault/IMultiVaultFacetWithdrawEvents.sol";

import "../storage/MultiVaultStorage.sol";
import "../../libraries/SafeERC20.sol";


abstract contract MultiVaultHelperWithdraw is IMultiVaultFacetWithdrawEvents {
    using SafeERC20 for IERC20;

    modifier withdrawalNotSeenBefore(bytes memory payload) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        bytes32 withdrawalId = keccak256(payload);

        require(!s.withdrawalIds[withdrawalId]);
        s.withdrawalIds[withdrawalId] = true;

        _;
    }

    function _processWithdrawEvent(
        bytes memory payload,
        bytes[] memory signatures,
        IEverscale.EverscaleAddress memory configuration
    ) internal view returns (IEverscale.EverscaleEvent memory) {
        MultiVaultStorage.Storage storage s = MultiVaultStorage._storage();

        require(
            IBridge(s.bridge).verifySignedEverscaleEvent(payload, signatures) == 0
        );

        // Decode Everscale event
        (IEverscale.EverscaleEvent memory _event) = abi.decode(payload, (IEverscale.EverscaleEvent));

        require(
            _event.configurationWid == configuration.wid &&
            _event.configurationAddress == configuration.addr
        );

        return _event;
    }

    function _withdraw(
        address recipient,
        uint amount,
        uint fee,
        IMultiVaultFacetTokens.TokenType tokenType,
        bytes32 payloadId,
        address token
    ) internal {
        if (tokenType == IMultiVaultFacetTokens.TokenType.Native) {
            IMultiVaultToken(token).mint(recipient, amount - fee);
        } else {
            IERC20(token).safeTransfer(recipient, amount - fee);
        }

        emit Withdraw(
            tokenType,
            payloadId,
            token,
            recipient,
            amount,
            fee
        );
    }
}

File 30 of 33 : MultiVaultStorage.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


import "../../interfaces/IEverscale.sol";
import "../../interfaces/multivault/IMultiVaultFacetTokens.sol";
import "../../interfaces/multivault/IMultiVaultFacetPendingWithdrawals.sol";
import "../../interfaces/multivault/IMultiVaultFacetLiquidity.sol";


library MultiVaultStorage {
    uint constant MAX_BPS = 10_000;
    uint constant FEE_LIMIT = MAX_BPS / 2;

    uint8 constant DECIMALS_LIMIT = 18;
    uint256 constant SYMBOL_LENGTH_LIMIT = 32;
    uint256 constant NAME_LENGTH_LIMIT = 32;

    string constant DEFAULT_NAME_PREFIX = '';
    string constant DEFAULT_SYMBOL_PREFIX = '';

    string constant DEFAULT_NAME_LP_PREFIX = 'Octus LP ';
    string constant DEFAULT_SYMBOL_LP_PREFIX = 'octLP';

    uint256 constant WITHDRAW_PERIOD_DURATION_IN_SECONDS = 60 * 60 * 24; // 24 hours

    // Previous version of the Vault contract was built with Upgradable Proxy Pattern, without using Diamond storage
    bytes32 constant MULTIVAULT_LEGACY_STORAGE_POSITION = 0x0000000000000000000000000000000000000000000000000000000000000002;

    uint constant LP_EXCHANGE_RATE_BPS = 10_000_000_000;

    struct Storage {
        mapping (address => IMultiVaultFacetTokens.Token) tokens_;
        mapping (address => IEverscale.EverscaleAddress) natives_;

        uint defaultNativeDepositFee;
        uint defaultNativeWithdrawFee;
        uint defaultAlienDepositFee;
        uint defaultAlienWithdrawFee;

        bool emergencyShutdown;

        address bridge;
        mapping(bytes32 => bool) withdrawalIds;
        IEverscale.EverscaleAddress rewards_;
        IEverscale.EverscaleAddress configurationNative_;
        IEverscale.EverscaleAddress configurationAlien_;

        address governance;
        address pendingGovernance;
        address guardian;
        address management;

        mapping (address => IMultiVaultFacetTokens.TokenPrefix) prefixes_;
        mapping (address => uint) fees;

        // STORAGE UPDATE 1
        // Pending withdrawals
        // - Counter pending withdrawals per user
        mapping(address => uint) pendingWithdrawalsPerUser;
        // - Pending withdrawal details
        mapping(address => mapping(uint256 => IMultiVaultFacetPendingWithdrawals.PendingWithdrawalParams)) pendingWithdrawals_;

        // - Total amount of pending withdrawals per token
        mapping(address => uint) pendingWithdrawalsTotal;

        // STORAGE UPDATE 2
        // Withdrawal limits per token
        mapping(address => IMultiVaultFacetPendingWithdrawals.WithdrawalLimits) withdrawalLimits_;

        // - Withdrawal periods. Each period is `WITHDRAW_PERIOD_DURATION_IN_SECONDS` seconds long.
        // If some period has reached the `withdrawalLimitPerPeriod` - all the future
        // withdrawals in this period require manual approve, see note on `setPendingWithdrawalsApprove`
        mapping(address => mapping(uint256 => IMultiVaultFacetPendingWithdrawals.WithdrawalPeriodParams)) withdrawalPeriods_;

        address withdrawGuardian;

        // STORAGE UPDATE 3
        mapping (address => IMultiVaultFacetLiquidity.Liquidity) liquidity;
        uint defaultInterest;

        // STORAGE UPDATE 4
        // - Receives native value, attached to the deposit
        address gasDonor;
        address weth;
    }

    function _storage() internal pure returns (Storage storage s) {
        assembly {
            s.slot := MULTIVAULT_LEGACY_STORAGE_POSITION
        }
    }
}

File 31 of 33 : MultiVaultStorageReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.0;


//import "@openzeppelin/contracts/security/ReentrancyGuard.sol";


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

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

    bytes32 constant REENTRANCY_GUARD_LEGACY_STORAGE_POSITION = 0x0000000000000000000000000000000000000000000000000000000000000001;

    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    function _storage() internal pure returns (ReentrancyGuardStorage storage s) {
        assembly {
            s.slot := REENTRANCY_GUARD_LEGACY_STORAGE_POSITION
        }
    }
}

File 32 of 33 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity 0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

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

pragma solidity 0.8.0;

import "./Context.sol";

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

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

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

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

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

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

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

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

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BlacklistTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BlacklistTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EarnTokenCash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EarnTokenFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lp_amount","type":"uint256"}],"name":"MintLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PendingWithdrawalCancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"payloadId","type":"bytes32"}],"name":"PendingWithdrawalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"PendingWithdrawalFill","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"PendingWithdrawalForce","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"enum IMultiVaultFacetPendingWithdrawals.ApproveStatus","name":"approveStatus","type":"uint8"}],"name":"PendingWithdrawalUpdateApproveStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bounty","type":"uint256"}],"name":"PendingWithdrawalUpdateBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PendingWithdrawalWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlying_amount","type":"uint256"}],"name":"RedeemLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SkimFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"activation","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isNative","type":"bool"},{"indexed":false,"internalType":"uint256","name":"depositFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawFee","type":"uint256"}],"name":"TokenActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"int8","name":"native_wid","type":"int8"},{"indexed":false,"internalType":"uint256","name":"native_addr","type":"uint256"},{"indexed":false,"internalType":"string","name":"name_prefix","type":"string"},{"indexed":false,"internalType":"string","name":"symbol_prefix","type":"string"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"TokenCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateDefaultAlienDepositFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateDefaultAlienWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inetrest","type":"uint256"}],"name":"UpdateDefaultLiquidityInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateDefaultNativeDepositFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateDefaultNativeWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateTokenDepositFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"UpdateTokenLiquidityInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"UpdateTokenWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IMultiVaultFacetTokens.TokenType","name":"_type","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"payloadId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"}],"name":"saveWithdrawAlien","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"bounty","type":"uint256"}],"name":"saveWithdrawAlien","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"}],"name":"saveWithdrawNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"withdrawalIds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50613c30806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000525760003560e01c806351ce9d211462000057578063a078085e1462000070578063dc7fc7c71462000087578063e063cb5014620000b6575b600080fd5b6200006e6200006836600462001f26565b620000cd565b005b6200006e6200008136600462001f8e565b620000df565b6200009e6200009836600462001f0d565b620005bd565b604051620000ad919062002594565b60405180910390f35b6200006e620000c736600462001f26565b620005e8565b620000db82826000620000df565b5050565b620000e962000783565b826000620000f6620007c6565b82516020808501919091206000818152600784019092526040909120549192509060ff16156200012557600080fd5b60008181526007830160205260408120805460ff191660011790556200014a620007c6565b600681015490915060ff16156200016057600080fd5b60006200016c620007c6565b60408051808201909152600c820154600090810b810b810b8252600d8301546020830152919250620001a2908a908a90620007cb565b90506000898051906020012090506000620001c18360400151620008d6565b905046816060015114620001d457600080fd5b80516001600160a01b031660009081526020859052604090206001015460ff1615620001ff57600080fd5b60006200021782602001518360000151600162000984565b90506200022982600001518262000a61565b60008183602001516200023d919062002996565b905060006200025b8460000151876020015163ffffffff1662000b9d565b90506200027c8460000151876020015163ffffffff16866020015162000c11565b600062000293856000015186602001518462000c74565b9050620002a4856000015162000d16565b856020015111158015620002b55750805b15620002f257620002d8856040015186602001518660018a8a6000015162000da1565b620002e4858462000eae565b5050505050505050620005aa565b604080860180516001600160a01b03908116600090815260148c016020528381205492519091168152918220805491926200032d8362002a1c565b909155505085516001600160a01b0316600090815260168a016020526040812080548692906200035f90849062002938565b9091555050838e11156200037257600080fd5b6040518060e0016040528087600001516001600160a01b0316815260200185815260200187604001516001600160a01b0316336001600160a01b031614620003bc576000620003be565b8f5b815260208a81015163ffffffff16908201526040016000815260200187606001518152602001876080015181525089601501600088604001516001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a81548160ff02191690836003811115620004ac57634e487b7160e01b600052602160045260246000fd5b021790555060a0820151600582015560c082015180516006830180546001600160a01b0319166001600160a01b039092169190911781556020808301518051620004fd926007870192019062001b26565b50604091820151600291909101805460ff191691151591909117905588810151895191517f7eceb0f3cb15a14a99150ee4ebb88b9a1df1b3c50efae84c8a82072d3264b4c994506200055893509091859189908d90620024fa565b60405180910390a181620005945762000594604051806040016040528088604001516001600160a01b0316815260200183815250600162000f4d565b620005a0868262001000565b5050505050505050505b50505050620005b86200104c565b505050565b600080620005ca620007c6565b60008481526007909101602052604090205460ff169150505b919050565b620005f262000783565b816000620005ff620007c6565b82516020808501919091206000818152600784019092526040909120549192509060ff16156200062e57600080fd5b60008181526007830160205260408120805460ff1916600117905562000653620007c6565b600681015490915060ff16156200066957600080fd5b600062000675620007c6565b60408051808201909152600a820154600090810b810b810b8252600b8301546020830152919250620006ab9089908990620007cb565b90506000888051906020012090506000620006ca83604001516200105f565b905046816080015114620006dd57600080fd5b6000620006ea826200112f565b6001600160a01b03811660009081526020879052604090206001015490915060ff16156200071757600080fd5b60006200072b836040015183600162000984565b905062000739828262000a61565b6200075283606001518460400151836000888762000da1565b6200076f8382856040015162000769919062002996565b6200120b565b50505050505050505050620000db6200104c565b60006200078f620012a4565b805490915060021415620007c05760405162461bcd60e51b8152600401620007b7906200277a565b60405180910390fd5b60029055565b600290565b620007d562001bb5565b6000620007e1620007c6565b600681015460405163dccb359960e01b815291925061010090046001600160a01b03169063dccb3599906200081d90889088906004016200259f565b60206040518083038186803b1580156200083657600080fd5b505afa1580156200084b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008719190620022e9565b63ffffffff16156200088257600080fd5b6000858060200190518101906200089a919062002120565b9050836000015160000b816060015160000b148015620008c1575083602001518160800151145b620008cb57600080fd5b9150505b9392505050565b620008e062001c02565b60008060008060008060008880602001905181019062000901919062002222565b96509650965096509650965096506040518060a00160405280886001600160a01b03168152602001876001600160801b03168152602001866001600160a01b031681526020018581526020016040518060600160405280866001600160a01b03168152602001858152602001841515815250815250975050505050505050919050565b60008062000991620007c6565b6001600160a01b03808616600090815260208381526040808320815160c08101835281548152600182015460ff90811615159482019490945260028201549281019290925260038101546060830152600401549182161515608082015261010090910490921660a0830152919250908085600181111562000a2257634e487b7160e01b600052602160045260246000fd5b1462000a3357816060015162000a39565b81604001515b905061271062000a4a888362002974565b62000a56919062002953565b979650505050505050565b600062000a6d620007c6565b90508162000a7c5750620000db565b6001600160a01b0383166000818152601a83016020818152604080842081516080810183528154808252600183015482860152600283015493820193909352600390910154606082015294845291905262000ad957508262000b1a565b600061271083606001518662000af0919062002974565b62000afc919062002953565b905062000b0a818662002996565b915062000b188682620012a9565b505b8062000b2957505050620000db565b6001600160a01b03851660009081526013840160205260408120805483929062000b5590849062002938565b90915550506040517f08048a94734163493ed404ed3c277587011804858159ded940ef190b79210e409062000b8e9087908490620024e1565b60405180910390a15050505050565b62000ba762001c4a565b600062000bb3620007c6565b6001600160a01b0385166000908152601882016020526040812091925062000bdb856200132c565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505091505092915050565b600062000c1d620007c6565b9050600062000c2c846200132c565b6001600160a01b0386166000908152601884016020908152604080832084845290915281208054929350859290919062000c6890849062002938565b90915550505050505050565b60008062000c81620007c6565b6001600160a01b03861660009081526017820160209081526040918290208251606081018452815481526001820154928101929092526002015460ff16151591810182905291925062000cda57600192505050620008cf565b80518510801562000d0c575060208082015190850151855162000cfe908862002938565b62000d0a919062002996565b105b9695505050505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319062000d479030906004016200243d565b60206040518083038186803b15801562000d6057600080fd5b505afa15801562000d75573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d9b9190620022d0565b92915050565b600083600181111562000dc457634e487b7160e01b600052602160045260246000fd5b141562000e40576001600160a01b0381166340c10f198762000de7878962002996565b6040518363ffffffff1660e01b815260040162000e06929190620024e1565b600060405180830381600087803b15801562000e2157600080fd5b505af115801562000e36573d6000803e3d6000fd5b5050505062000e63565b62000e638662000e51868862002996565b6001600160a01b03841691906200133d565b7f66a5e8ae5d3d5883f7d5a0caaa6b4e484e40278a749b23c63830e86aa54c1c6e83838389898960405162000e9e9695949392919062002615565b60405180910390a1505050505050565b6080820151516001600160a01b03811630141562000ecb57600080fd5b6001600160a01b03811615620005b857600063f4161d8b60e01b848460405160240162000efa929190620027b1565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526080860151805192015190925062000f479190839062001397565b50505050565b600062000f59620007c6565b83516001600160a01b03166000908152601582016020908152604080832082880151845290915290206004018054919250839160ff1916600183600381111562000fb357634e487b7160e01b600052602160045260246000fd5b0217905550825160208401516040517f7ed894a371451167c714ecffd40c5a3faf1e0ec592087257a0ea2cce2af664199262000ff392909186906200255c565b60405180910390a1505050565b6080820151516001600160a01b0381163014156200101d57600080fd5b6001600160a01b03811615620005b857600063328ab0d960e01b848460405160240162000efa929190620027b1565b600062001058620012a4565b6001905550565b6200106962001c64565b60008060008060008060008060008060008c80602001905181019062001090919062001fff565b604080516101008101825260009c909c0b60c08d0190815260e08d019b909b52998b52895160608082018c5299815260208181019990995260ff909716878b0152878b01969096526001600160801b03909416898901526001600160a01b03928316878a01526080890191909152865195860187521684529183019190915215159181019190915260a08201529b505050505050505050505050919050565b6000806200113c620007c6565b905060006200114f84600001516200140d565b6001600160a01b038116600090815260208490526040902054909150620011ce57620011848460000151856020015162001474565b5062001192816001620017d6565b83516001600160a01b0382166000908152600180850160209081526040832084518154940b60ff1660ff19909416939093178355909201519101555b6001600160a01b03808216600090815260208490526040902060040154610100900416801562001203579250620005e3915050565b509392505050565b60a0820151516001600160a01b0381163014156200122857600080fd5b6001600160a01b03811615620005b857600063ede7310d60e01b84846040516024016200125792919062002813565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925260a0860151805192015190925062000f479190839062001397565b600190565b6000620012b5620007c6565b905081620012c45750620000db565b6001600160a01b0383166000908152601a8201602052604081206002018054849290620012f390849062002938565b90915550506040517f27f709cebd025c144ca5c2ace6b8b708999e4bd14a82106cfce6c1991cf9f5179062000ff39085908590620024e1565b600062000d9b620151808362002953565b620005b88363a9059cbb60e01b84846040516024016200135f929190620024e1565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915262001944565b6000836001600160a01b031683604051620013b3919062002380565b6000604051808303816000865af19150503d8060008114620013f2576040519150601f19603f3d011682016040523d82523d6000602084013e620013f7565b606091505b50509050811562000f47578062000f4757600080fd5b600030826000015183602001516040516020016200142d9291906200239e565b6040516020818303038152906040528051906020012060405160200162001456929190620023e7565b60408051601f19818403018152919052805160209091012092915050565b60008062001481620007c6565b9050600060405180602001620014979062001cb5565b601f1982820381018352601f9091011660408190528651602088810151939450600093620014c8939091016200239e565b604051602081830303815290604052805190602001209050808251602084016000f593506000836012016000866001600160a01b03166001600160a01b03168152602001908152602001600020604051806060016040529081600082015481526020016001820180546200153c90620029df565b80601f01602080910402602001604051908101604052809291908181526020018280546200156a90620029df565b8015620015bb5780601f106200158f57610100808354040283529160200191620015bb565b820191906000526020600020905b8154815290600101906020018083116200159d57829003601f168201915b50505050508152602001600282018054620015d690620029df565b80601f01602080910402602001604051908101604052809291908181526020018280546200160490620029df565b8015620016555780601f10620016295761010080835404028352916020019162001655565b820191906000526020600020905b8154815290600101906020018083116200163757829003601f168201915b5050505050815250509050600081600001516000146200167a5781602001516200168b565b604051806020016040528060008152505b825190915060009015620016a4578260400151620016b5565b604051806020016040528060008152505b9050866001600160a01b0316631624f6c6838a60000151604051602001620016df929190620023b4565b604051602081830303815290604052838b6020015160405160200162001707929190620023b4565b60408051601f19818403018152828252908d01516001600160e01b031960e086901b1683526200173b939260040162002676565b600060405180830381600087803b1580156200175657600080fd5b505af11580156200176b573d6000803e3d6000fd5b505050507f3e6b76d22037074e0415e75179bad8e98a6c869a4edc4f01b3375e2c72faf9a6878a600001518b6020015185858d600001518e602001518f60400151604051620017c298979695949392919062002451565b60405180910390a150505050505092915050565b6000620017e2620007c6565b9050600082620017f7578160040154620017fd565b81600201545b90506000836200181257826005015462001818565b82600301545b90506040518060c00160405280438152602001600015158152602001838152602001828152602001851515815260200160006001600160a01b0316815250836000016000876001600160a01b03166001600160a01b031681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff021916908315150217905550604082015181600201556060820151816003015560808201518160040160006101000a81548160ff02191690831515021790555060a08201518160040160016101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507f0c051f0fa97177b7a4bae4cd3f0c633d0813e1da77361496a35b6ff7e117ea31854386858560405162000b8e9594939291906200252e565b60006200199b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620019db9092919063ffffffff16565b805190915015620005b85780806020019051810190620019bc919062001ef0565b620005b85760405162461bcd60e51b8152600401620007b79062002730565b6060620019ec8484600085620019f4565b949350505050565b60608247101562001a195760405162461bcd60e51b8152600401620007b790620026b3565b62001a248562001ab7565b62001a435760405162461bcd60e51b8152600401620007b790620026f9565b600080866001600160a01b0316858760405162001a61919062002380565b60006040518083038185875af1925050503d806000811462001aa0576040519150601f19603f3d011682016040523d82523d6000602084013e62001aa5565b606091505b509150915062000a5682828662001ae8565b600080826001600160a01b0316803b806020016040519081016040528181526000908060200190933c511192915050565b6060831562001af9575081620008cf565b82511562001b0a5782518084602001fd5b8160405162461bcd60e51b8152600401620007b7919062002661565b82805462001b3490620029df565b90600052602060002090601f01602090048101928262001b58576000855562001ba3565b82601f1062001b7357805160ff191683800117855562001ba3565b8280016001018555821562001ba3579182015b8281111562001ba357825182559160200191906001019062001b86565b5062001bb192915062001cc3565b5090565b604080516101208101825260008082526020820181905260609282018390529181018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b6040518060a0016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200162001c4562001cda565b905290565b604051806040016040528060008152602001600081525090565b6040518060c0016040528062001c7962001d06565b815260200162001c8862001d1d565b81526020016000815260200160006001600160a01b031681526020016000815260200162001c4562001cda565b6111658062002a9683390190565b5b8082111562001bb1576000815560010162001cc4565b604051806060016040528060006001600160a01b03168152602001606081526020016000151581525090565b604080518082019091526000808252602082015290565b60405180606001604052806060815260200160608152602001600060ff1681525090565b8051620005e38162002a7c565b600082601f83011262001d5f578081fd5b8135602067ffffffffffffffff82111562001d7e5762001d7e62002a66565b62001d8d8182840201620028e0565b82815281810190858301855b8581101562001dc65762001db3898684358b010162001de4565b8452928401929084019060010162001d99565b5090979650505050505050565b80518015158114620005e357600080fd5b600082601f83011262001df5578081fd5b813562001e0c62001e06826200290d565b620028e0565b81815284602083860101111562001e21578283fd5b816020850160208301379081016020019190915292915050565b600082601f83011262001e4c578081fd5b815162001e5d62001e06826200290d565b81815284602083860101111562001e72578283fd5b620019ec826020830160208701620029b0565b8051600081900b8114620005e357600080fd5b80516001600160801b0381168114620005e357600080fd5b805163ffffffff81168114620005e357600080fd5b805167ffffffffffffffff81168114620005e357600080fd5b805160ff81168114620005e357600080fd5b60006020828403121562001f02578081fd5b620008cf8262001dd3565b60006020828403121562001f1f578081fd5b5035919050565b6000806040838503121562001f39578081fd5b823567ffffffffffffffff8082111562001f51578283fd5b62001f5f8683870162001de4565b9350602085013591508082111562001f75578283fd5b5062001f848582860162001d4e565b9150509250929050565b60008060006060848603121562001fa3578081fd5b833567ffffffffffffffff8082111562001fbb578283fd5b62001fc98783880162001de4565b9450602086013591508082111562001fdf578283fd5b5062001fee8682870162001d4e565b925050604084013590509250925092565b60008060008060008060008060008060006101608c8e03121562002021578687fd5b6200202c8c62001e85565b9a5060208c0151995060408c015167ffffffffffffffff8111156200204f578788fd5b6200205d8e828f0162001e3b565b99505060608c015167ffffffffffffffff8111156200207a578788fd5b620020888e828f0162001e3b565b9850506200209960808d0162001ede565b9650620020a960a08d0162001e98565b9550620020b960c08d0162001d41565b945060e08c01519350620020d16101008d0162001d41565b92506101208c015167ffffffffffffffff811115620020ee578283fd5b620020fc8e828f0162001e3b565b9250506200210e6101408d0162001dd3565b90509295989b509295989b9093969950565b60006020828403121562002132578081fd5b815167ffffffffffffffff808211156200214a578283fd5b818401915061012080838703121562002161578384fd5b6200216c81620028e0565b9050620021798362001ec5565b8152620021896020840162001eb0565b6020820152604083015182811115620021a0578485fd5b620021ae8782860162001e3b565b604083015250620021c26060840162001e85565b606082015260808301516080820152620021df60a0840162001e85565b60a082015260c083015160c0820152620021fc60e0840162001d41565b60e082015261010091506200221382840162001eb0565b91810191909152949350505050565b600080600080600080600060e0888a0312156200223d578081fd5b87516200224a8162002a7c565b96506200225a6020890162001e98565b955060408801516200226c8162002a7c565b606089015160808a01519196509450620022868162002a7c565b60a089015190935067ffffffffffffffff811115620022a3578182fd5b620022b18a828b0162001e3b565b925050620022c260c0890162001dd3565b905092959891949750929550565b600060208284031215620022e2578081fd5b5051919050565b600060208284031215620022fb578081fd5b620008cf8262001eb0565b6001600160a01b03169052565b600081518084526200232d816020860160208601620029b0565b601f01601f19169290920160200192915050565b600060018060a01b0382511683526020820151606060208501526200236a606085018262002313565b6040938401511515949093019390935250919050565b6000825162002394818460208701620029b0565b9190910192915050565b60009290920b60f81b8252600182015260210190565b60008351620023c8818460208801620029b0565b835190830190620023de818360208801620029b0565b01949350505050565b6001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f192c19818bebb5c6c95f5dcb3c3257379fc46fb654780cb06f3211ee77e1a360603582015260550190565b6001600160a01b0391909116815260200190565b6001600160a01b0389168152600088810b60208301526040820188905261010060608301819052620024868382018962002313565b905082810360808401526200249c818862002313565b905082810360a0840152620024b2818762002313565b905082810360c0840152620024c8818662002313565b91505060ff831660e08301529998505050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b03958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b03959095168552602085019390935290151560408401526060830152608082015260a00190565b6001600160a01b038416815260208101839052606081016004831062002586576200258662002a50565b826040830152949350505050565b901515815260200190565b600060408252620025b4604083018562002313565b602083820381850152818551808452828401915082838202850101838801865b838110156200260657601f19878403018552620025f383835162002313565b94860194925090850190600101620025d4565b50909998505050505050505050565b60c08101600288106200262c576200262c62002a50565b96815260208101959095526001600160a01b039384166040860152919092166060840152608083019190915260a09091015290565b600060208252620008cf602083018462002313565b6000606082526200268b606083018662002313565b82810360208401526200269f818662002313565b91505060ff83166040830152949350505050565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60006040825260018060a01b038085511660408401526020850151606084015280604086015116608084015250606084015160a0830152608084015160a060c08401526200280360e084018262002341565b9150508260208301529392505050565b60006040825283518051820b60408401526020810151606084015250602084015160e06080840152805160606101208501526200285561018085018262002313565b9050602082015161011f198583030161014086015262002876828262002313565b60409384015160ff166101608701529287015160a08601525050606085015190620028a560c085018362002306565b608086015160e085015260a0860151848203603f19016101008601529150620028cf818362002341565b925050508260208301529392505050565b60405181810167ffffffffffffffff8111828210171562002905576200290562002a66565b604052919050565b600067ffffffffffffffff8211156200292a576200292a62002a66565b50601f01601f191660200190565b600082198211156200294e576200294e62002a3a565b500190565b6000826200296f57634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161562002991576200299162002a3a565b500290565b600082821015620029ab57620029ab62002a3a565b500390565b60005b83811015620029cd578181015183820152602001620029b3565b8381111562000f475750506000910152565b600281046001821680620029f457607f821691505b6020821081141562002a1657634e487b7160e01b600052602260045260246000fd5b50919050565b600060001982141562002a335762002a3362002a3a565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811462002a9257600080fd5b5056fe608060405234801561001057600080fd5b50610019610028565b6100228161002c565b5061007c565b3390565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6110da8061008b6000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c806370a08231116100a25780639dc29fac116100715780639dc29fac146101fe578063a457c2d714610211578063a9059cbb14610224578063dd62ed3e14610237578063f2fde38b1461024a5761010b565b806370a08231146101c6578063715018a6146101d95780638da5cb5b146101e157806395d89b41146101f65761010b565b806323b872dd116100de57806323b872dd14610178578063313ce5671461018b57806339509351146101a057806340c10f19146101b35761010b565b806306fdde0314610110578063095ea7b31461012e5780631624f6c61461014e57806318160ddd14610163575b600080fd5b61011861025d565b6040516101259190610c9f565b60405180910390f35b61014161013c366004610bdd565b6102ef565b6040516101259190610c94565b61016161015c366004610c06565b610311565b005b61016b610362565b6040516101259190610ff7565b610141610186366004610ba2565b610368565b610193610396565b6040516101259190611000565b6101416101ae366004610bdd565b61039f565b6101616101c1366004610bdd565b6103cb565b61016b6101d4366004610b4f565b610421565b610161610440565b6101e961048b565b6040516101259190610c80565b61011861049a565b61016161020c366004610bdd565b6104a9565b61014161021f366004610bdd565b6104f2565b610141610232366004610bdd565b61053a565b61016b610245366004610b70565b610552565b610161610258366004610b4f565b61057d565b60606005805461026c9061103d565b80601f01602080910402602001604051908101604052809291908181526020018280546102989061103d565b80156102e55780601f106102ba576101008083540402835291602001916102e5565b820191906000526020600020905b8154815290600101906020018083116102c857829003601f168201915b5050505050905090565b6000806102fa6105ee565b90506103078185856105f2565b5060019392505050565b6001541561031e57600080fd5b8251610331906005906020860190610a1e565b508151610345906006906020850190610a1e565b506007805460ff191660ff92909216919091179055505043600155565b60045490565b6000806103736105ee565b90506103808582856106a6565b61038b8585856106f0565b506001949350505050565b60075460ff1690565b6000806103aa6105ee565b90506103078185856103bc8589610552565b6103c6919061100e565b6105f2565b6103d36105ee565b6001600160a01b03166103e461048b565b6001600160a01b0316146104135760405162461bcd60e51b815260040161040a90610e7c565b60405180910390fd5b61041d8282610814565b5050565b6001600160a01b0381166000908152600260205260409020545b919050565b6104486105ee565b6001600160a01b031661045961048b565b6001600160a01b03161461047f5760405162461bcd60e51b815260040161040a90610e7c565b61048960006108dc565b565b6000546001600160a01b031690565b60606006805461026c9061103d565b6104b16105ee565b6001600160a01b03166104c261048b565b6001600160a01b0316146104e85760405162461bcd60e51b815260040161040a90610e7c565b61041d828261092c565b6000806104fd6105ee565b9050600061050b8286610552565b90508381101561052d5760405162461bcd60e51b815260040161040a90610f7b565b61038b82868684036105f2565b6000806105456105ee565b90506103078185856106f0565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b6105856105ee565b6001600160a01b031661059661048b565b6001600160a01b0316146105bc5760405162461bcd60e51b815260040161040a90610e7c565b6001600160a01b0381166105e25760405162461bcd60e51b815260040161040a90610d77565b6105eb816108dc565b50565b3390565b6001600160a01b0383166106185760405162461bcd60e51b815260040161040a90610f37565b6001600160a01b03821661063e5760405162461bcd60e51b815260040161040a90610dbd565b6001600160a01b0380841660008181526003602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610699908590610ff7565b60405180910390a3505050565b60006106b28484610552565b905060001981146106ea57818110156106dd5760405162461bcd60e51b815260040161040a90610dff565b6106ea84848484036105f2565b50505050565b6001600160a01b0383166107165760405162461bcd60e51b815260040161040a90610ef2565b6001600160a01b03821661073c5760405162461bcd60e51b815260040161040a90610cf2565b610747838383610a19565b6001600160a01b038316600090815260026020526040902054818110156107805760405162461bcd60e51b815260040161040a90610e36565b6001600160a01b038085166000908152600260205260408082208585039055918516815290812080548492906107b790849061100e565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108019190610ff7565b60405180910390a36106ea848484610a19565b6001600160a01b03821661083a5760405162461bcd60e51b815260040161040a90610fc0565b61084660008383610a19565b8060046000828254610858919061100e565b90915550506001600160a01b0382166000908152600260205260408120805483929061088590849061100e565b90915550506040516001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906108c8908590610ff7565b60405180910390a361041d60008383610a19565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0382166109525760405162461bcd60e51b815260040161040a90610eb1565b61095e82600083610a19565b6001600160a01b038216600090815260026020526040902054818110156109975760405162461bcd60e51b815260040161040a90610d35565b6001600160a01b03831660009081526002602052604081208383039055600480548492906109c6908490611026565b90915550506040516000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610a09908690610ff7565b60405180910390a3610a19836000845b505050565b828054610a2a9061103d565b90600052602060002090601f016020900481019282610a4c5760008555610a92565b82601f10610a6557805160ff1916838001178555610a92565b82800160010185558215610a92579182015b82811115610a92578251825591602001919060010190610a77565b50610a9e929150610aa2565b5090565b5b80821115610a9e5760008155600101610aa3565b80356001600160a01b038116811461043b57600080fd5b600082601f830112610ade578081fd5b813567ffffffffffffffff80821115610af957610af961108e565b604051601f8301601f191681016020018281118282101715610b1d57610b1d61108e565b604052828152848301602001861015610b34578384fd5b82602086016020830137918201602001929092529392505050565b600060208284031215610b60578081fd5b610b6982610ab7565b9392505050565b60008060408385031215610b82578081fd5b610b8b83610ab7565b9150610b9960208401610ab7565b90509250929050565b600080600060608486031215610bb6578081fd5b610bbf84610ab7565b9250610bcd60208501610ab7565b9150604084013590509250925092565b60008060408385031215610bef578182fd5b610bf883610ab7565b946020939093013593505050565b600080600060608486031215610c1a578283fd5b833567ffffffffffffffff80821115610c31578485fd5b610c3d87838801610ace565b94506020860135915080821115610c52578384fd5b50610c5f86828701610ace565b925050604084013560ff81168114610c75578182fd5b809150509250925092565b6001600160a01b0391909116815260200190565b901515815260200190565b6000602080835283518082850152825b81811015610ccb57858101830151858201604001528201610caf565b81811115610cdc5783604083870101525b50601f01601f1916929092016040019392505050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201526265737360e81b606082015260800190565b60208082526022908201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604082015261636560f01b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604082015261737360f01b606082015260800190565b6020808252601d908201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604082015260600190565b60208082526026908201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604082015265616c616e636560d01b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526021908201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736040820152607360f81b606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604082015264647265737360d81b606082015260800190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526025908201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604082015264207a65726f60d81b606082015260800190565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b90815260200190565b60ff91909116815260200190565b6000821982111561102157611021611078565b500190565b60008282101561103857611038611078565b500390565b60028104600182168061105157607f821691505b6020821081141561107257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220f18026eb95ae5bbcf4ea7ea2edc428bd95ed8a4f562ba16ae901cac10b082dc664736f6c63430008000033a2646970667358221220c9598748da7a628e64081ce47d0cfee0537ff3ac83018e4a9d3fc654d7d501d864736f6c63430008000033

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620000525760003560e01c806351ce9d211462000057578063a078085e1462000070578063dc7fc7c71462000087578063e063cb5014620000b6575b600080fd5b6200006e6200006836600462001f26565b620000cd565b005b6200006e6200008136600462001f8e565b620000df565b6200009e6200009836600462001f0d565b620005bd565b604051620000ad919062002594565b60405180910390f35b6200006e620000c736600462001f26565b620005e8565b620000db82826000620000df565b5050565b620000e962000783565b826000620000f6620007c6565b82516020808501919091206000818152600784019092526040909120549192509060ff16156200012557600080fd5b60008181526007830160205260408120805460ff191660011790556200014a620007c6565b600681015490915060ff16156200016057600080fd5b60006200016c620007c6565b60408051808201909152600c820154600090810b810b810b8252600d8301546020830152919250620001a2908a908a90620007cb565b90506000898051906020012090506000620001c18360400151620008d6565b905046816060015114620001d457600080fd5b80516001600160a01b031660009081526020859052604090206001015460ff1615620001ff57600080fd5b60006200021782602001518360000151600162000984565b90506200022982600001518262000a61565b60008183602001516200023d919062002996565b905060006200025b8460000151876020015163ffffffff1662000b9d565b90506200027c8460000151876020015163ffffffff16866020015162000c11565b600062000293856000015186602001518462000c74565b9050620002a4856000015162000d16565b856020015111158015620002b55750805b15620002f257620002d8856040015186602001518660018a8a6000015162000da1565b620002e4858462000eae565b5050505050505050620005aa565b604080860180516001600160a01b03908116600090815260148c016020528381205492519091168152918220805491926200032d8362002a1c565b909155505085516001600160a01b0316600090815260168a016020526040812080548692906200035f90849062002938565b9091555050838e11156200037257600080fd5b6040518060e0016040528087600001516001600160a01b0316815260200185815260200187604001516001600160a01b0316336001600160a01b031614620003bc576000620003be565b8f5b815260208a81015163ffffffff16908201526040016000815260200187606001518152602001876080015181525089601501600088604001516001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a81548160ff02191690836003811115620004ac57634e487b7160e01b600052602160045260246000fd5b021790555060a0820151600582015560c082015180516006830180546001600160a01b0319166001600160a01b039092169190911781556020808301518051620004fd926007870192019062001b26565b50604091820151600291909101805460ff191691151591909117905588810151895191517f7eceb0f3cb15a14a99150ee4ebb88b9a1df1b3c50efae84c8a82072d3264b4c994506200055893509091859189908d90620024fa565b60405180910390a181620005945762000594604051806040016040528088604001516001600160a01b0316815260200183815250600162000f4d565b620005a0868262001000565b5050505050505050505b50505050620005b86200104c565b505050565b600080620005ca620007c6565b60008481526007909101602052604090205460ff169150505b919050565b620005f262000783565b816000620005ff620007c6565b82516020808501919091206000818152600784019092526040909120549192509060ff16156200062e57600080fd5b60008181526007830160205260408120805460ff1916600117905562000653620007c6565b600681015490915060ff16156200066957600080fd5b600062000675620007c6565b60408051808201909152600a820154600090810b810b810b8252600b8301546020830152919250620006ab9089908990620007cb565b90506000888051906020012090506000620006ca83604001516200105f565b905046816080015114620006dd57600080fd5b6000620006ea826200112f565b6001600160a01b03811660009081526020879052604090206001015490915060ff16156200071757600080fd5b60006200072b836040015183600162000984565b905062000739828262000a61565b6200075283606001518460400151836000888762000da1565b6200076f8382856040015162000769919062002996565b6200120b565b50505050505050505050620000db6200104c565b60006200078f620012a4565b805490915060021415620007c05760405162461bcd60e51b8152600401620007b7906200277a565b60405180910390fd5b60029055565b600290565b620007d562001bb5565b6000620007e1620007c6565b600681015460405163dccb359960e01b815291925061010090046001600160a01b03169063dccb3599906200081d90889088906004016200259f565b60206040518083038186803b1580156200083657600080fd5b505afa1580156200084b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008719190620022e9565b63ffffffff16156200088257600080fd5b6000858060200190518101906200089a919062002120565b9050836000015160000b816060015160000b148015620008c1575083602001518160800151145b620008cb57600080fd5b9150505b9392505050565b620008e062001c02565b60008060008060008060008880602001905181019062000901919062002222565b96509650965096509650965096506040518060a00160405280886001600160a01b03168152602001876001600160801b03168152602001866001600160a01b031681526020018581526020016040518060600160405280866001600160a01b03168152602001858152602001841515815250815250975050505050505050919050565b60008062000991620007c6565b6001600160a01b03808616600090815260208381526040808320815160c08101835281548152600182015460ff90811615159482019490945260028201549281019290925260038101546060830152600401549182161515608082015261010090910490921660a0830152919250908085600181111562000a2257634e487b7160e01b600052602160045260246000fd5b1462000a3357816060015162000a39565b81604001515b905061271062000a4a888362002974565b62000a56919062002953565b979650505050505050565b600062000a6d620007c6565b90508162000a7c5750620000db565b6001600160a01b0383166000818152601a83016020818152604080842081516080810183528154808252600183015482860152600283015493820193909352600390910154606082015294845291905262000ad957508262000b1a565b600061271083606001518662000af0919062002974565b62000afc919062002953565b905062000b0a818662002996565b915062000b188682620012a9565b505b8062000b2957505050620000db565b6001600160a01b03851660009081526013840160205260408120805483929062000b5590849062002938565b90915550506040517f08048a94734163493ed404ed3c277587011804858159ded940ef190b79210e409062000b8e9087908490620024e1565b60405180910390a15050505050565b62000ba762001c4a565b600062000bb3620007c6565b6001600160a01b0385166000908152601882016020526040812091925062000bdb856200132c565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505091505092915050565b600062000c1d620007c6565b9050600062000c2c846200132c565b6001600160a01b0386166000908152601884016020908152604080832084845290915281208054929350859290919062000c6890849062002938565b90915550505050505050565b60008062000c81620007c6565b6001600160a01b03861660009081526017820160209081526040918290208251606081018452815481526001820154928101929092526002015460ff16151591810182905291925062000cda57600192505050620008cf565b80518510801562000d0c575060208082015190850151855162000cfe908862002938565b62000d0a919062002996565b105b9695505050505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319062000d479030906004016200243d565b60206040518083038186803b15801562000d6057600080fd5b505afa15801562000d75573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d9b9190620022d0565b92915050565b600083600181111562000dc457634e487b7160e01b600052602160045260246000fd5b141562000e40576001600160a01b0381166340c10f198762000de7878962002996565b6040518363ffffffff1660e01b815260040162000e06929190620024e1565b600060405180830381600087803b15801562000e2157600080fd5b505af115801562000e36573d6000803e3d6000fd5b5050505062000e63565b62000e638662000e51868862002996565b6001600160a01b03841691906200133d565b7f66a5e8ae5d3d5883f7d5a0caaa6b4e484e40278a749b23c63830e86aa54c1c6e83838389898960405162000e9e9695949392919062002615565b60405180910390a1505050505050565b6080820151516001600160a01b03811630141562000ecb57600080fd5b6001600160a01b03811615620005b857600063f4161d8b60e01b848460405160240162000efa929190620027b1565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526080860151805192015190925062000f479190839062001397565b50505050565b600062000f59620007c6565b83516001600160a01b03166000908152601582016020908152604080832082880151845290915290206004018054919250839160ff1916600183600381111562000fb357634e487b7160e01b600052602160045260246000fd5b0217905550825160208401516040517f7ed894a371451167c714ecffd40c5a3faf1e0ec592087257a0ea2cce2af664199262000ff392909186906200255c565b60405180910390a1505050565b6080820151516001600160a01b0381163014156200101d57600080fd5b6001600160a01b03811615620005b857600063328ab0d960e01b848460405160240162000efa929190620027b1565b600062001058620012a4565b6001905550565b6200106962001c64565b60008060008060008060008060008060008c80602001905181019062001090919062001fff565b604080516101008101825260009c909c0b60c08d0190815260e08d019b909b52998b52895160608082018c5299815260208181019990995260ff909716878b0152878b01969096526001600160801b03909416898901526001600160a01b03928316878a01526080890191909152865195860187521684529183019190915215159181019190915260a08201529b505050505050505050505050919050565b6000806200113c620007c6565b905060006200114f84600001516200140d565b6001600160a01b038116600090815260208490526040902054909150620011ce57620011848460000151856020015162001474565b5062001192816001620017d6565b83516001600160a01b0382166000908152600180850160209081526040832084518154940b60ff1660ff19909416939093178355909201519101555b6001600160a01b03808216600090815260208490526040902060040154610100900416801562001203579250620005e3915050565b509392505050565b60a0820151516001600160a01b0381163014156200122857600080fd5b6001600160a01b03811615620005b857600063ede7310d60e01b84846040516024016200125792919062002813565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925260a0860151805192015190925062000f479190839062001397565b600190565b6000620012b5620007c6565b905081620012c45750620000db565b6001600160a01b0383166000908152601a8201602052604081206002018054849290620012f390849062002938565b90915550506040517f27f709cebd025c144ca5c2ace6b8b708999e4bd14a82106cfce6c1991cf9f5179062000ff39085908590620024e1565b600062000d9b620151808362002953565b620005b88363a9059cbb60e01b84846040516024016200135f929190620024e1565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915262001944565b6000836001600160a01b031683604051620013b3919062002380565b6000604051808303816000865af19150503d8060008114620013f2576040519150601f19603f3d011682016040523d82523d6000602084013e620013f7565b606091505b50509050811562000f47578062000f4757600080fd5b600030826000015183602001516040516020016200142d9291906200239e565b6040516020818303038152906040528051906020012060405160200162001456929190620023e7565b60408051601f19818403018152919052805160209091012092915050565b60008062001481620007c6565b9050600060405180602001620014979062001cb5565b601f1982820381018352601f9091011660408190528651602088810151939450600093620014c8939091016200239e565b604051602081830303815290604052805190602001209050808251602084016000f593506000836012016000866001600160a01b03166001600160a01b03168152602001908152602001600020604051806060016040529081600082015481526020016001820180546200153c90620029df565b80601f01602080910402602001604051908101604052809291908181526020018280546200156a90620029df565b8015620015bb5780601f106200158f57610100808354040283529160200191620015bb565b820191906000526020600020905b8154815290600101906020018083116200159d57829003601f168201915b50505050508152602001600282018054620015d690620029df565b80601f01602080910402602001604051908101604052809291908181526020018280546200160490620029df565b8015620016555780601f10620016295761010080835404028352916020019162001655565b820191906000526020600020905b8154815290600101906020018083116200163757829003601f168201915b5050505050815250509050600081600001516000146200167a5781602001516200168b565b604051806020016040528060008152505b825190915060009015620016a4578260400151620016b5565b604051806020016040528060008152505b9050866001600160a01b0316631624f6c6838a60000151604051602001620016df929190620023b4565b604051602081830303815290604052838b6020015160405160200162001707929190620023b4565b60408051601f19818403018152828252908d01516001600160e01b031960e086901b1683526200173b939260040162002676565b600060405180830381600087803b1580156200175657600080fd5b505af11580156200176b573d6000803e3d6000fd5b505050507f3e6b76d22037074e0415e75179bad8e98a6c869a4edc4f01b3375e2c72faf9a6878a600001518b6020015185858d600001518e602001518f60400151604051620017c298979695949392919062002451565b60405180910390a150505050505092915050565b6000620017e2620007c6565b9050600082620017f7578160040154620017fd565b81600201545b90506000836200181257826005015462001818565b82600301545b90506040518060c00160405280438152602001600015158152602001838152602001828152602001851515815260200160006001600160a01b0316815250836000016000876001600160a01b03166001600160a01b031681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff021916908315150217905550604082015181600201556060820151816003015560808201518160040160006101000a81548160ff02191690831515021790555060a08201518160040160016101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507f0c051f0fa97177b7a4bae4cd3f0c633d0813e1da77361496a35b6ff7e117ea31854386858560405162000b8e9594939291906200252e565b60006200199b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620019db9092919063ffffffff16565b805190915015620005b85780806020019051810190620019bc919062001ef0565b620005b85760405162461bcd60e51b8152600401620007b79062002730565b6060620019ec8484600085620019f4565b949350505050565b60608247101562001a195760405162461bcd60e51b8152600401620007b790620026b3565b62001a248562001ab7565b62001a435760405162461bcd60e51b8152600401620007b790620026f9565b600080866001600160a01b0316858760405162001a61919062002380565b60006040518083038185875af1925050503d806000811462001aa0576040519150601f19603f3d011682016040523d82523d6000602084013e62001aa5565b606091505b509150915062000a5682828662001ae8565b600080826001600160a01b0316803b806020016040519081016040528181526000908060200190933c511192915050565b6060831562001af9575081620008cf565b82511562001b0a5782518084602001fd5b8160405162461bcd60e51b8152600401620007b7919062002661565b82805462001b3490620029df565b90600052602060002090601f01602090048101928262001b58576000855562001ba3565b82601f1062001b7357805160ff191683800117855562001ba3565b8280016001018555821562001ba3579182015b8281111562001ba357825182559160200191906001019062001b86565b5062001bb192915062001cc3565b5090565b604080516101208101825260008082526020820181905260609282018390529181018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b6040518060a0016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200162001c4562001cda565b905290565b604051806040016040528060008152602001600081525090565b6040518060c0016040528062001c7962001d06565b815260200162001c8862001d1d565b81526020016000815260200160006001600160a01b031681526020016000815260200162001c4562001cda565b6111658062002a9683390190565b5b8082111562001bb1576000815560010162001cc4565b604051806060016040528060006001600160a01b03168152602001606081526020016000151581525090565b604080518082019091526000808252602082015290565b60405180606001604052806060815260200160608152602001600060ff1681525090565b8051620005e38162002a7c565b600082601f83011262001d5f578081fd5b8135602067ffffffffffffffff82111562001d7e5762001d7e62002a66565b62001d8d8182840201620028e0565b82815281810190858301855b8581101562001dc65762001db3898684358b010162001de4565b8452928401929084019060010162001d99565b5090979650505050505050565b80518015158114620005e357600080fd5b600082601f83011262001df5578081fd5b813562001e0c62001e06826200290d565b620028e0565b81815284602083860101111562001e21578283fd5b816020850160208301379081016020019190915292915050565b600082601f83011262001e4c578081fd5b815162001e5d62001e06826200290d565b81815284602083860101111562001e72578283fd5b620019ec826020830160208701620029b0565b8051600081900b8114620005e357600080fd5b80516001600160801b0381168114620005e357600080fd5b805163ffffffff81168114620005e357600080fd5b805167ffffffffffffffff81168114620005e357600080fd5b805160ff81168114620005e357600080fd5b60006020828403121562001f02578081fd5b620008cf8262001dd3565b60006020828403121562001f1f578081fd5b5035919050565b6000806040838503121562001f39578081fd5b823567ffffffffffffffff8082111562001f51578283fd5b62001f5f8683870162001de4565b9350602085013591508082111562001f75578283fd5b5062001f848582860162001d4e565b9150509250929050565b60008060006060848603121562001fa3578081fd5b833567ffffffffffffffff8082111562001fbb578283fd5b62001fc98783880162001de4565b9450602086013591508082111562001fdf578283fd5b5062001fee8682870162001d4e565b925050604084013590509250925092565b60008060008060008060008060008060006101608c8e03121562002021578687fd5b6200202c8c62001e85565b9a5060208c0151995060408c015167ffffffffffffffff8111156200204f578788fd5b6200205d8e828f0162001e3b565b99505060608c015167ffffffffffffffff8111156200207a578788fd5b620020888e828f0162001e3b565b9850506200209960808d0162001ede565b9650620020a960a08d0162001e98565b9550620020b960c08d0162001d41565b945060e08c01519350620020d16101008d0162001d41565b92506101208c015167ffffffffffffffff811115620020ee578283fd5b620020fc8e828f0162001e3b565b9250506200210e6101408d0162001dd3565b90509295989b509295989b9093969950565b60006020828403121562002132578081fd5b815167ffffffffffffffff808211156200214a578283fd5b818401915061012080838703121562002161578384fd5b6200216c81620028e0565b9050620021798362001ec5565b8152620021896020840162001eb0565b6020820152604083015182811115620021a0578485fd5b620021ae8782860162001e3b565b604083015250620021c26060840162001e85565b606082015260808301516080820152620021df60a0840162001e85565b60a082015260c083015160c0820152620021fc60e0840162001d41565b60e082015261010091506200221382840162001eb0565b91810191909152949350505050565b600080600080600080600060e0888a0312156200223d578081fd5b87516200224a8162002a7c565b96506200225a6020890162001e98565b955060408801516200226c8162002a7c565b606089015160808a01519196509450620022868162002a7c565b60a089015190935067ffffffffffffffff811115620022a3578182fd5b620022b18a828b0162001e3b565b925050620022c260c0890162001dd3565b905092959891949750929550565b600060208284031215620022e2578081fd5b5051919050565b600060208284031215620022fb578081fd5b620008cf8262001eb0565b6001600160a01b03169052565b600081518084526200232d816020860160208601620029b0565b601f01601f19169290920160200192915050565b600060018060a01b0382511683526020820151606060208501526200236a606085018262002313565b6040938401511515949093019390935250919050565b6000825162002394818460208701620029b0565b9190910192915050565b60009290920b60f81b8252600182015260210190565b60008351620023c8818460208801620029b0565b835190830190620023de818360208801620029b0565b01949350505050565b6001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f192c19818bebb5c6c95f5dcb3c3257379fc46fb654780cb06f3211ee77e1a360603582015260550190565b6001600160a01b0391909116815260200190565b6001600160a01b0389168152600088810b60208301526040820188905261010060608301819052620024868382018962002313565b905082810360808401526200249c818862002313565b905082810360a0840152620024b2818762002313565b905082810360c0840152620024c8818662002313565b91505060ff831660e08301529998505050505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b03958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b03959095168552602085019390935290151560408401526060830152608082015260a00190565b6001600160a01b038416815260208101839052606081016004831062002586576200258662002a50565b826040830152949350505050565b901515815260200190565b600060408252620025b4604083018562002313565b602083820381850152818551808452828401915082838202850101838801865b838110156200260657601f19878403018552620025f383835162002313565b94860194925090850190600101620025d4565b50909998505050505050505050565b60c08101600288106200262c576200262c62002a50565b96815260208101959095526001600160a01b039384166040860152919092166060840152608083019190915260a09091015290565b600060208252620008cf602083018462002313565b6000606082526200268b606083018662002313565b82810360208401526200269f818662002313565b91505060ff83166040830152949350505050565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60006040825260018060a01b038085511660408401526020850151606084015280604086015116608084015250606084015160a0830152608084015160a060c08401526200280360e084018262002341565b9150508260208301529392505050565b60006040825283518051820b60408401526020810151606084015250602084015160e06080840152805160606101208501526200285561018085018262002313565b9050602082015161011f198583030161014086015262002876828262002313565b60409384015160ff166101608701529287015160a08601525050606085015190620028a560c085018362002306565b608086015160e085015260a0860151848203603f19016101008601529150620028cf818362002341565b925050508260208301529392505050565b60405181810167ffffffffffffffff8111828210171562002905576200290562002a66565b604052919050565b600067ffffffffffffffff8211156200292a576200292a62002a66565b50601f01601f191660200190565b600082198211156200294e576200294e62002a3a565b500190565b6000826200296f57634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161562002991576200299162002a3a565b500290565b600082821015620029ab57620029ab62002a3a565b500390565b60005b83811015620029cd578181015183820152602001620029b3565b8381111562000f475750506000910152565b600281046001821680620029f457607f821691505b6020821081141562002a1657634e487b7160e01b600052602260045260246000fd5b50919050565b600060001982141562002a335762002a3362002a3a565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811462002a9257600080fd5b5056fe608060405234801561001057600080fd5b50610019610028565b6100228161002c565b5061007c565b3390565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6110da8061008b6000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c806370a08231116100a25780639dc29fac116100715780639dc29fac146101fe578063a457c2d714610211578063a9059cbb14610224578063dd62ed3e14610237578063f2fde38b1461024a5761010b565b806370a08231146101c6578063715018a6146101d95780638da5cb5b146101e157806395d89b41146101f65761010b565b806323b872dd116100de57806323b872dd14610178578063313ce5671461018b57806339509351146101a057806340c10f19146101b35761010b565b806306fdde0314610110578063095ea7b31461012e5780631624f6c61461014e57806318160ddd14610163575b600080fd5b61011861025d565b6040516101259190610c9f565b60405180910390f35b61014161013c366004610bdd565b6102ef565b6040516101259190610c94565b61016161015c366004610c06565b610311565b005b61016b610362565b6040516101259190610ff7565b610141610186366004610ba2565b610368565b610193610396565b6040516101259190611000565b6101416101ae366004610bdd565b61039f565b6101616101c1366004610bdd565b6103cb565b61016b6101d4366004610b4f565b610421565b610161610440565b6101e961048b565b6040516101259190610c80565b61011861049a565b61016161020c366004610bdd565b6104a9565b61014161021f366004610bdd565b6104f2565b610141610232366004610bdd565b61053a565b61016b610245366004610b70565b610552565b610161610258366004610b4f565b61057d565b60606005805461026c9061103d565b80601f01602080910402602001604051908101604052809291908181526020018280546102989061103d565b80156102e55780601f106102ba576101008083540402835291602001916102e5565b820191906000526020600020905b8154815290600101906020018083116102c857829003601f168201915b5050505050905090565b6000806102fa6105ee565b90506103078185856105f2565b5060019392505050565b6001541561031e57600080fd5b8251610331906005906020860190610a1e565b508151610345906006906020850190610a1e565b506007805460ff191660ff92909216919091179055505043600155565b60045490565b6000806103736105ee565b90506103808582856106a6565b61038b8585856106f0565b506001949350505050565b60075460ff1690565b6000806103aa6105ee565b90506103078185856103bc8589610552565b6103c6919061100e565b6105f2565b6103d36105ee565b6001600160a01b03166103e461048b565b6001600160a01b0316146104135760405162461bcd60e51b815260040161040a90610e7c565b60405180910390fd5b61041d8282610814565b5050565b6001600160a01b0381166000908152600260205260409020545b919050565b6104486105ee565b6001600160a01b031661045961048b565b6001600160a01b03161461047f5760405162461bcd60e51b815260040161040a90610e7c565b61048960006108dc565b565b6000546001600160a01b031690565b60606006805461026c9061103d565b6104b16105ee565b6001600160a01b03166104c261048b565b6001600160a01b0316146104e85760405162461bcd60e51b815260040161040a90610e7c565b61041d828261092c565b6000806104fd6105ee565b9050600061050b8286610552565b90508381101561052d5760405162461bcd60e51b815260040161040a90610f7b565b61038b82868684036105f2565b6000806105456105ee565b90506103078185856106f0565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b6105856105ee565b6001600160a01b031661059661048b565b6001600160a01b0316146105bc5760405162461bcd60e51b815260040161040a90610e7c565b6001600160a01b0381166105e25760405162461bcd60e51b815260040161040a90610d77565b6105eb816108dc565b50565b3390565b6001600160a01b0383166106185760405162461bcd60e51b815260040161040a90610f37565b6001600160a01b03821661063e5760405162461bcd60e51b815260040161040a90610dbd565b6001600160a01b0380841660008181526003602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610699908590610ff7565b60405180910390a3505050565b60006106b28484610552565b905060001981146106ea57818110156106dd5760405162461bcd60e51b815260040161040a90610dff565b6106ea84848484036105f2565b50505050565b6001600160a01b0383166107165760405162461bcd60e51b815260040161040a90610ef2565b6001600160a01b03821661073c5760405162461bcd60e51b815260040161040a90610cf2565b610747838383610a19565b6001600160a01b038316600090815260026020526040902054818110156107805760405162461bcd60e51b815260040161040a90610e36565b6001600160a01b038085166000908152600260205260408082208585039055918516815290812080548492906107b790849061100e565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108019190610ff7565b60405180910390a36106ea848484610a19565b6001600160a01b03821661083a5760405162461bcd60e51b815260040161040a90610fc0565b61084660008383610a19565b8060046000828254610858919061100e565b90915550506001600160a01b0382166000908152600260205260408120805483929061088590849061100e565b90915550506040516001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906108c8908590610ff7565b60405180910390a361041d60008383610a19565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0382166109525760405162461bcd60e51b815260040161040a90610eb1565b61095e82600083610a19565b6001600160a01b038216600090815260026020526040902054818110156109975760405162461bcd60e51b815260040161040a90610d35565b6001600160a01b03831660009081526002602052604081208383039055600480548492906109c6908490611026565b90915550506040516000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610a09908690610ff7565b60405180910390a3610a19836000845b505050565b828054610a2a9061103d565b90600052602060002090601f016020900481019282610a4c5760008555610a92565b82601f10610a6557805160ff1916838001178555610a92565b82800160010185558215610a92579182015b82811115610a92578251825591602001919060010190610a77565b50610a9e929150610aa2565b5090565b5b80821115610a9e5760008155600101610aa3565b80356001600160a01b038116811461043b57600080fd5b600082601f830112610ade578081fd5b813567ffffffffffffffff80821115610af957610af961108e565b604051601f8301601f191681016020018281118282101715610b1d57610b1d61108e565b604052828152848301602001861015610b34578384fd5b82602086016020830137918201602001929092529392505050565b600060208284031215610b60578081fd5b610b6982610ab7565b9392505050565b60008060408385031215610b82578081fd5b610b8b83610ab7565b9150610b9960208401610ab7565b90509250929050565b600080600060608486031215610bb6578081fd5b610bbf84610ab7565b9250610bcd60208501610ab7565b9150604084013590509250925092565b60008060408385031215610bef578182fd5b610bf883610ab7565b946020939093013593505050565b600080600060608486031215610c1a578283fd5b833567ffffffffffffffff80821115610c31578485fd5b610c3d87838801610ace565b94506020860135915080821115610c52578384fd5b50610c5f86828701610ace565b925050604084013560ff81168114610c75578182fd5b809150509250925092565b6001600160a01b0391909116815260200190565b901515815260200190565b6000602080835283518082850152825b81811015610ccb57858101830151858201604001528201610caf565b81811115610cdc5783604083870101525b50601f01601f1916929092016040019392505050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201526265737360e81b606082015260800190565b60208082526022908201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604082015261636560f01b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604082015261737360f01b606082015260800190565b6020808252601d908201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604082015260600190565b60208082526026908201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604082015265616c616e636560d01b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526021908201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736040820152607360f81b606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604082015264647265737360d81b606082015260800190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526025908201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604082015264207a65726f60d81b606082015260800190565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b90815260200190565b60ff91909116815260200190565b6000821982111561102157611021611078565b500190565b60008282101561103857611038611078565b500390565b60028104600182168061105157607f821691505b6020821081141561107257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220f18026eb95ae5bbcf4ea7ea2edc428bd95ed8a4f562ba16ae901cac10b082dc664736f6c63430008000033a2646970667358221220c9598748da7a628e64081ce47d0cfee0537ff3ac83018e4a9d3fc654d7d501d864736f6c63430008000033

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.