ETH Price: $3,221.98 (+3.49%)

Contract

0x27d54D93c8F89988E20fC8FFD911218bAc44a0F6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create ERC4626167844222023-03-08 15:26:59678 days ago1678289219IN
0x27d54D93...bAc44a0F6
0 ETH0.0741113629.17985227
Create ERC4626167223362023-02-27 21:54:11686 days ago1677534851IN
0x27d54D93...bAc44a0F6
0 ETH0.0785854631
Create ERC4626162411252022-12-22 15:25:35754 days ago1671722735IN
0x27d54D93...bAc44a0F6
0 ETH0.0518801820.42091198
Create ERC4626162411192022-12-22 15:24:23754 days ago1671722663IN
0x27d54D93...bAc44a0F6
0 ETH0.0551455921.75417432
Create ERC4626162411142022-12-22 15:23:23754 days ago1671722603IN
0x27d54D93...bAc44a0F6
0 ETH0.0558818221.99785164
Create ERC4626162408562022-12-22 14:31:23754 days ago1671719483IN
0x27d54D93...bAc44a0F6
0 ETH0.062197524.40301808

Latest 6 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
167844222023-03-08 15:26:59678 days ago1678289219
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
167223362023-02-27 21:54:11686 days ago1677534851
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
162411252022-12-22 15:25:35754 days ago1671722735
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
162411192022-12-22 15:24:23754 days ago1671722663
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
162411142022-12-22 15:23:23754 days ago1671722603
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
162408562022-12-22 14:31:23754 days ago1671719483
0x27d54D93...bAc44a0F6
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StakeableEulerERC4626Factory

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 14 : StakeableEulerERC4626Factory.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {Owned} from "solmate/auth/Owned.sol";

import {ERC4626Factory} from "../../base/ERC4626Factory.sol";
import {IEulerEToken} from "../external/IEulerEToken.sol";
import {IEulerMarkets} from "../external/IEulerMarkets.sol";
import {IRewardsDistribution} from "../external/IRewardsDistribution.sol";
import {StakeableEulerERC4626} from "./StakeableEulerERC4626.sol";

/// @title EulerERC4626Factory
/// @author Sam Bugs
/// @notice Factory for creating EulerERC4626 contracts, that can handle staking
contract StakeableEulerERC4626Factory is ERC4626Factory, Owned {
    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    /// @notice Thrown when trying to deploy an EulerERC4626 vault using an asset without an eToken
    error StakeableEulerERC4626Factory__ETokenNonexistent();

    /// -----------------------------------------------------------------------
    /// Immutable params
    /// -----------------------------------------------------------------------

    /// @notice The Euler main contract address
    /// @dev Target of ERC20 approval when depositing
    address public immutable euler;

    /// @notice The Euler markets module address
    IEulerMarkets public immutable markets;

    /// @notice The rewards distribution address
    IRewardsDistribution public immutable rewardsDistribution;

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    constructor(address euler_, IEulerMarkets markets_, IRewardsDistribution rewardsDistribution_, address owner_) Owned(owner_) {
        euler = euler_;
        markets = markets_;
        rewardsDistribution = rewardsDistribution_;
    }

    /// -----------------------------------------------------------------------
    /// Staking functions
    /// -----------------------------------------------------------------------

    /// @notice Returns how much was earned during staking
    function reward(StakeableEulerERC4626 vault) public view returns (address rewardsToken, uint256 earned) {
        return vault.reward();
    }

    /// @notice Allows owner to set or update a new staking contract. Will claim rewards from previous staking if available
    function updateStakingAddress(StakeableEulerERC4626 vault, uint256 rewardIndex, address recipient) external onlyOwner {
        vault.updateStakingAddress(rewardIndex, recipient);
    }

     /// @notice Allows owner to claim rewards and stop staking all together
    function stopStaking(StakeableEulerERC4626 vault, address recipient) external onlyOwner {
        vault.stopStaking(recipient);
    }

    /// @notice Allows owner to stake a certain amount of tokens
    function stake(StakeableEulerERC4626 vault, uint256 amount) external onlyOwner {
        vault.stake(amount);
    }

    /// @notice Allows owner to unstake a certain amount of tokens
    function unstake(StakeableEulerERC4626 vault, uint256 amount) external onlyOwner {
        vault.unstake(amount);
    }

    /// @notice Allows owner to claim all staking rewards
    function claimReward(StakeableEulerERC4626 vault, address recipient) public onlyOwner returns (address rewardsToken, uint256 earned) {
        return vault.claimReward(recipient);
    }

    /// -----------------------------------------------------------------------
    /// External functions
    /// -----------------------------------------------------------------------

    /// @inheritdoc ERC4626Factory
    function createERC4626(ERC20 asset) external virtual override returns (ERC4626 vault) {
        address eTokenAddress = markets.underlyingToEToken(address(asset));
        if (eTokenAddress == address(0)) {
            revert StakeableEulerERC4626Factory__ETokenNonexistent();
        }

        vault = new StakeableEulerERC4626{salt: bytes32(0)}(asset, euler, IEulerEToken(eTokenAddress), rewardsDistribution, address(this));

        emit CreateERC4626(asset, vault);
    }

    /// @inheritdoc ERC4626Factory
    function computeERC4626Address(ERC20 asset) external view virtual override returns (ERC4626 vault) {
        vault = ERC4626(
            _computeCreate2Address(
                keccak256(
                    abi.encodePacked(
                        // Deployment bytecode:
                        type(StakeableEulerERC4626).creationCode,
                        // Constructor arguments:
                        abi.encode(asset, euler, IEulerEToken(markets.underlyingToEToken(address(asset))), rewardsDistribution, address(this))
                    )
                )
            )
        );
    }
}

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

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

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

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

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

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

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

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

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

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

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

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

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

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

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

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

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

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnerUpdated(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

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

    constructor(address _owner) {
        owner = _owner;

        emit OwnerUpdated(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function setOwner(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}

File 5 of 14 : ERC4626Factory.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {Bytes32AddressLib} from "solmate/utils/Bytes32AddressLib.sol";

/// @title ERC4626Factory
/// @author zefram.eth
/// @notice Abstract base contract for deploying ERC4626 wrappers
/// @dev Uses CREATE2 deterministic deployment, so there can only be a single
/// vault for each asset.
abstract contract ERC4626Factory {
    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------

    using Bytes32AddressLib for bytes32;

    /// -----------------------------------------------------------------------
    /// Events
    /// -----------------------------------------------------------------------

    /// @notice Emitted when a new ERC4626 vault has been created
    /// @param asset The base asset used by the vault
    /// @param vault The vault that was created
    event CreateERC4626(ERC20 indexed asset, ERC4626 vault);

    /// -----------------------------------------------------------------------
    /// External functions
    /// -----------------------------------------------------------------------

    /// @notice Creates an ERC4626 vault for an asset
    /// @dev Uses CREATE2 deterministic deployment, so there can only be a single
    /// vault for each asset. Will revert if a vault has already been deployed for the asset.
    /// @param asset The base asset used by the vault
    /// @return vault The vault that was created
    function createERC4626(ERC20 asset) external virtual returns (ERC4626 vault);

    /// @notice Computes the address of the ERC4626 vault corresponding to an asset. Returns
    /// a valid result regardless of whether the vault has already been deployed.
    /// @param asset The base asset used by the vault
    /// @return vault The vault corresponding to the asset
    function computeERC4626Address(ERC20 asset) external view virtual returns (ERC4626 vault);

    /// -----------------------------------------------------------------------
    /// Internal functions
    /// -----------------------------------------------------------------------

    /// @notice Computes the address of a contract deployed by this factory using CREATE2, given
    /// the bytecode hash of the contract. Can also be used to predict addresses of contracts yet to
    /// be deployed.
    /// @dev Always uses bytes32(0) as the salt
    /// @param bytecodeHash The keccak256 hash of the creation code of the contract being deployed concatenated
    /// with the ABI-encoded constructor arguments.
    /// @return The address of the deployed contract
    function _computeCreate2Address(bytes32 bytecodeHash) internal view virtual returns (address) {
        return keccak256(abi.encodePacked(bytes1(0xFF), address(this), bytes32(0), bytecodeHash))
            // Prefix:
            // Creator:
            // Salt:
            // Bytecode hash:
            .fromLast20Bytes(); // Convert the CREATE2 hash into an address.
    }
}

File 6 of 14 : IEulerEToken.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

/// @notice Tokenised representation of assets
interface IEulerEToken {

    // @notice Retrieve the current allowance
    /// @param holder Xor with the desired sub-account ID (if applicable)
    /// @param spender Trusted address
    function allowance(address holder, address spender) external view returns (uint);

    /// @notice Address of underlying asset
    function underlyingAsset() external view returns (address);

    /// @notice Balance of a particular account, in underlying units (increases as interest is earned)
    function balanceOfUnderlying(address account) external view returns (uint256);

    /// @notice Balance of a particular account
    function balanceOf(address account) external view returns (uint256);

    /// @notice Transfer underlying tokens from sender to the Euler pool, and increase account's eTokens
    /// @param subAccountId 0 for primary, 1-255 for a sub-account
    /// @param amount In underlying units (use max uint256 for full underlying token balance)
    function deposit(uint256 subAccountId, uint256 amount) external;

    /// @notice Transfer underlying tokens from Euler pool to sender, and decrease account's eTokens
    /// @param subAccountId 0 for primary, 1-255 for a sub-account
    /// @param amount In underlying units (use max uint256 for full pool balance)
    function withdraw(uint256 subAccountId, uint256 amount) external;

    /// @notice Convert an eToken balance to an underlying amount, taking into account current exchange rate
    /// @param balance eToken balance, in internal book-keeping units (18 decimals)
    /// @return Amount in underlying units, (same decimals as underlying token)
    function convertBalanceToUnderlying(uint balance) external view returns (uint);

    /// @notice Convert an underlying amount to an eToken balance, taking into account current exchange rate
    /// @param underlyingAmount Amount in underlying units (same decimals as underlying token)
    /// @return eToken balance, in internal book-keeping units (18 decimals)
    function convertUnderlyingToBalance(uint underlyingAmount) external view returns (uint);
}

File 7 of 14 : IEulerMarkets.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

/// @notice Activating and querying markets, and maintaining entered markets lists
interface IEulerMarkets {
    /// @notice Given an underlying, lookup the associated EToken
    /// @param underlying Token address
    /// @return EToken address, or address(0) if not activated
    function underlyingToEToken(address underlying) external view returns (address);
}

File 8 of 14 : IRewardsDistribution.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// Original contract can be found under the following link:
// https://github.com/Synthetixio/synthetix/blob/master/contracts/interfaces/IRewardsDistribution.sol
interface IRewardsDistribution {
    // Structs
    struct DistributionData {
        address destination;
        uint amount;
    }

    // Views
    function distributions(uint256 index) external view returns (DistributionData memory);
}

File 9 of 14 : StakeableEulerERC4626.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {Owned} from "solmate/auth/Owned.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";

import {IEulerEToken} from "../external/IEulerEToken.sol";
import {IRewardsDistribution} from "../external/IRewardsDistribution.sol";
import {IStakingRewards} from "../external/IStakingRewards.sol";

import {EulerERC4626} from "../EulerERC4626.sol";

/// @title StakeableEulerERC4626
/// @author Sam Bugs
/// @notice A ERC4626 wrapper for Euler Finance, that can handle staking
contract StakeableEulerERC4626 is EulerERC4626, Owned {

    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    /// @notice Thrown when trying to assign an invalid rewards contract
    error StakeableEulerERC4626__InvalidRewardContract();

    /// -----------------------------------------------------------------------
    /// Libraries usage
    /// -----------------------------------------------------------------------

    using SafeTransferLib for ERC20;

    /// -----------------------------------------------------------------------
    /// Immutable params
    /// -----------------------------------------------------------------------

    /// @notice The rewards distribution address
    IRewardsDistribution public immutable rewardsDistribution;

    /// -----------------------------------------------------------------------
    /// Mutable params
    /// -----------------------------------------------------------------------

    /// @notice The staking rewards address
    IStakingRewards public stakingRewards;

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    constructor(ERC20 asset_, address euler_, IEulerEToken eToken_, IRewardsDistribution rewardsDistribution_, address owner_)
        EulerERC4626(asset_, euler_, eToken_)
        Owned(owner_)
    {
        rewardsDistribution = rewardsDistribution_;
    }

    /// -----------------------------------------------------------------------
    /// ERC4626 overrides
    /// -----------------------------------------------------------------------

    function totalAssets() public view virtual override returns (uint256) {
        uint256 eTokenStakedBalance = _getStakedBalance();
        if (eTokenStakedBalance > 0) {
            // We add all eToken balance (staked and non-staked) and then convert to underlying
            // We do this to prevent rounding differences if we converted to underlying and then added the results
            uint256 eTokenBalanceInContract = eToken.balanceOf(address(this));
            return eToken.convertBalanceToUnderlying(eTokenStakedBalance + eTokenBalanceInContract);
        }
        return eToken.balanceOfUnderlying(address(this));
    }

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual override {
        uint256 underlyingBalanceInContract = eToken.balanceOfUnderlying(address(this));
        if (underlyingBalanceInContract < assets) {
            // Need to unstake to meet the demand
            uint256 neededUnderlying = assets - underlyingBalanceInContract;
            uint256 neededEToken = eToken.convertUnderlyingToBalance(neededUnderlying);

            // We also withdraw 5% of remaining staked eTokens so that we can avoid unstaking again in the next withdraw
            uint256 eTokenStakedBalance = _getStakedBalance();
            uint256 remaining = eTokenStakedBalance - neededEToken;
            uint256 withdrawAttempt = neededEToken + FixedPointMathLib.mulDivUp(remaining, 5, 100);

            // We make sure we don't try to withdraw more than available
            uint256 toWithdraw = eTokenStakedBalance < withdrawAttempt ? eTokenStakedBalance : withdrawAttempt;

            stakingRewards.withdraw(toWithdraw);
        }
        super.beforeWithdraw(assets, shares);
    }

    /// -----------------------------------------------------------------------
    /// Staking functions
    /// -----------------------------------------------------------------------

    /// @notice Returns how much was earned during staking
    function reward() public view returns (address rewardsToken, uint256 earned) {
        return _calculateReward(stakingRewards);
    }

    /// @notice Allows owner to set or update a new staking contract. Will claim rewards from previous staking if available
    function updateStakingAddress(uint256 rewardIndex, address recipient) external onlyOwner {
        _stopStaking(recipient);

        IRewardsDistribution.DistributionData memory data = rewardsDistribution.distributions(rewardIndex);
        IStakingRewards stakingRewards_ = IStakingRewards(data.destination);
        if (stakingRewards_.stakingToken() != address(eToken)) revert StakeableEulerERC4626__InvalidRewardContract();

        stakingRewards = stakingRewards_;
        ERC20(address(eToken)).safeApprove(address(stakingRewards_), type(uint256).max);
    }

     /// @notice Allows owner to claim rewards and stop staking all together
    function stopStaking(address recipient) external onlyOwner {
        _stopStaking(recipient);
        stakingRewards = IStakingRewards(address(0));
    }

    /// @notice Allows owner to stake a certain amount of tokens
    function stake(uint256 amount) external onlyOwner {
        stakingRewards.stake(amount);
    }

    /// @notice Allows owner to unstake a certain amount of tokens
    function unstake(uint256 amount) external onlyOwner {
        stakingRewards.withdraw(amount);
    }

    /// @notice Allows owner to claim all staking rewards
    function claimReward(address recipient) public onlyOwner returns (address rewardsToken, uint256 earned) {
        (rewardsToken, earned) = reward();
        stakingRewards.getReward();
        _transferRewardToken(rewardsToken, earned, recipient);
    }

    /// -----------------------------------------------------------------------
    /// Internal functions
    /// -----------------------------------------------------------------------

    function _getStakedBalance() internal view returns (uint256 eTokenStakedBalance) {
        IStakingRewards stakingRewards_ = stakingRewards;
        if (address(stakingRewards_) != address(0)) {
            return stakingRewards_.balanceOf(address(this));
        }
    } 

    function _calculateReward(IStakingRewards stakingRewards_) public view returns (address rewardToken, uint256 earned) {
        if (address(stakingRewards_) != address(0)) {
            rewardToken = stakingRewards_.rewardsToken();
            earned = stakingRewards_.earned(address(this));
        }
    }

    function _stopStaking(address recipient) internal {
        IStakingRewards stakingRewards_ = stakingRewards;
        if (address(stakingRewards_) != address(0)) {
            ERC20(address(eToken)).safeApprove(address(stakingRewards_), 0);
            (address rewardToken, uint256 earned) = _calculateReward(stakingRewards_);
            stakingRewards_.exit();
            _transferRewardToken(rewardToken, earned, recipient);
        }
    }

    function _transferRewardToken(address rewardToken, uint256 amount, address recipient) internal {
        if (amount > 0) {
            ERC20(rewardToken).safeTransfer(recipient, amount);
        }
    }
}

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

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

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

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

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

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

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

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

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

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

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

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

/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
    function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {
        return address(uint160(uint256(bytesValue)));
    }

    function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {
        return bytes32(bytes20(addressValue));
    }
}

File 13 of 14 : IStakingRewards.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// Original contract can be found under the following link:
// https://github.com/Synthetixio/synthetix/blob/master/contracts/interfaces/IStakingRewards.sol
interface IStakingRewards {

    // Views
    function rewardsToken() external view returns (address);
    function stakingToken() external view returns (address);
    function balanceOf(address account) external view returns (uint256);
    function earned(address account) external view returns (uint256);

    // Mutative
    function exit() external;
    function getReward() external;
    function stake(uint256 amount) external;
    function withdraw(uint256 amount) external;
}

File 14 of 14 : EulerERC4626.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";

import {IEulerEToken} from "./external/IEulerEToken.sol";

/// @title EulerERC4626
/// @author zefram.eth
/// @notice ERC4626 wrapper for Euler Finance
contract EulerERC4626 is ERC4626 {
    /// -----------------------------------------------------------------------
    /// Libraries usage
    /// -----------------------------------------------------------------------

    using SafeTransferLib for ERC20;

    /// -----------------------------------------------------------------------
    /// Immutable params
    /// -----------------------------------------------------------------------

    /// @notice The Euler main contract address
    /// @dev Target of ERC20 approval when depositing
    address public immutable euler;

    /// @notice The Euler eToken contract
    IEulerEToken public immutable eToken;

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    constructor(ERC20 asset_, address euler_, IEulerEToken eToken_)
        ERC4626(asset_, _vaultName(asset_), _vaultSymbol(asset_))
    {
        euler = euler_;
        eToken = eToken_;
    }

    /// -----------------------------------------------------------------------
    /// ERC4626 overrides
    /// -----------------------------------------------------------------------

    function totalAssets() public view virtual override returns (uint256) {
        return eToken.balanceOfUnderlying(address(this));
    }

    function beforeWithdraw(uint256 assets, uint256 /*shares*/ ) internal virtual override {
        /// -----------------------------------------------------------------------
        /// Withdraw assets from Euler
        /// -----------------------------------------------------------------------

        eToken.withdraw(0, assets);
    }

    function afterDeposit(uint256 assets, uint256 /*shares*/ ) internal virtual override {
        /// -----------------------------------------------------------------------
        /// Deposit assets into Euler
        /// -----------------------------------------------------------------------

        // approve to euler
        asset.safeApprove(address(euler), assets);

        // deposit into eToken
        eToken.deposit(0, assets);
    }

    function maxWithdraw(address owner) public view override returns (uint256) {
        uint256 cash = asset.balanceOf(euler);
        uint256 assetsBalance = convertToAssets(balanceOf[owner]);
        return cash < assetsBalance ? cash : assetsBalance;
    }

    function maxRedeem(address owner) public view override returns (uint256) {
        uint256 cash = asset.balanceOf(euler);
        uint256 cashInShares = convertToShares(cash);
        uint256 shareBalance = balanceOf[owner];
        return cashInShares < shareBalance ? cashInShares : shareBalance;
    }

    /// -----------------------------------------------------------------------
    /// ERC20 metadata generation
    /// -----------------------------------------------------------------------

    function _vaultName(ERC20 asset_) internal view virtual returns (string memory vaultName) {
        vaultName = string.concat("ERC4626-Wrapped Euler ", asset_.symbol());
    }

    function _vaultSymbol(ERC20 asset_) internal view virtual returns (string memory vaultSymbol) {
        vaultSymbol = string.concat("we", asset_.symbol());
    }
}

Settings
{
  "remappings": [
    "create3-factory/=lib/create3-factory/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "src/=src/",
    "script/=script/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"euler_","type":"address"},{"internalType":"contract IEulerMarkets","name":"markets_","type":"address"},{"internalType":"contract IRewardsDistribution","name":"rewardsDistribution_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"StakeableEulerERC4626Factory__ETokenNonexistent","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract ERC20","name":"asset","type":"address"},{"indexed":false,"internalType":"contract ERC4626","name":"vault","type":"address"}],"name":"CreateERC4626","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"claimReward","outputs":[{"internalType":"address","name":"rewardsToken","type":"address"},{"internalType":"uint256","name":"earned","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"asset","type":"address"}],"name":"computeERC4626Address","outputs":[{"internalType":"contract ERC4626","name":"vault","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"asset","type":"address"}],"name":"createERC4626","outputs":[{"internalType":"contract ERC4626","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"euler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"markets","outputs":[{"internalType":"contract IEulerMarkets","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"}],"name":"reward","outputs":[{"internalType":"address","name":"rewardsToken","type":"address"},{"internalType":"uint256","name":"earned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDistribution","outputs":[{"internalType":"contract IRewardsDistribution","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"stopStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract StakeableEulerERC4626","name":"vault","type":"address"},{"internalType":"uint256","name":"rewardIndex","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"updateStakingAddress","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e060405234801561001057600080fd5b506040516200476e3803806200476e833981016040819052610031916100b0565b600080546001600160a01b0319166001600160a01b03831690811782556040518392907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350506001600160a01b0392831660805290821660a0521660c05261010f565b6001600160a01b03811681146100ad57600080fd5b50565b600080600080608085870312156100c657600080fd5b84516100d181610098565b60208601519094506100e281610098565b60408601519093506100f381610098565b606086015190925061010481610098565b939692955090935050565b60805160a05160c0516146056200016960003960008181610167015281816107b2015261097301526000818161011501528181610713015261088c015260008181610295015281816106f1015261095101526146056000f3fe60806040523480156200001157600080fd5b5060043610620000f15760003560e01c806380cfde3c1162000097578063adc9772e116200006e578063adc9772e146200024a578063af44af5b1462000261578063c2a672e01462000278578063db661b71146200028f57600080fd5b806380cfde3c14620001fb5780638da5cb5b1462000212578063abeccaa4146200023357600080fd5b80634953c78211620000cc5780634953c78214620001895780636353586b14620001cd5780637dc353e114620001e457600080fd5b806313af403514620000f65780633ac2a032146200010f5780633fc6df6e1462000161575b600080fd5b6200010d6200010736600462000dc9565b620002b7565b005b620001377f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001377f000000000000000000000000000000000000000000000000000000000000000081565b620001a06200019a36600462000df0565b620003ae565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091520162000158565b620001a0620001de36600462000dc9565b620004d3565b6200010d620001f536600462000e2e565b62000550565b620001376200020c36600462000dc9565b62000662565b600054620001379073ffffffffffffffffffffffffffffffffffffffff1681565b620001376200024436600462000dc9565b62000842565b6200010d6200025b36600462000e75565b62000a56565b6200010d6200027236600462000df0565b62000b60565b6200010d6200028936600462000e75565b62000c38565b620001377f000000000000000000000000000000000000000000000000000000000000000081565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200033e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331462000434576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fd279c19100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015285169063d279c1919060240160408051808303816000875af1158015620004a2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c8919062000ea4565b915091509250929050565b6000808273ffffffffffffffffffffffffffffffffffffffff1663228cb7336040518163ffffffff1660e01b81526004016040805180830381865afa15801562000521573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000547919062000ea4565b91509150915091565b60005473ffffffffffffffffffffffffffffffffffffffff163314620005d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fe0bd67750000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff828116602483015284169063e0bd677590604401600060405180830381600087803b1580156200064457600080fd5b505af115801562000659573d6000803e3d6000fd5b50505050505050565b60006200083c604051806020016200067a9062000d95565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f9091011660408190527f8948874900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015285917f0000000000000000000000000000000000000000000000000000000000000000917f00000000000000000000000000000000000000000000000000000000000000001690638948874990602401602060405180830381865afa1580156200075b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000781919062000ed5565b6040805173ffffffffffffffffffffffffffffffffffffffff948516602082015292841690830152821660608201527f000000000000000000000000000000000000000000000000000000000000000090911660808201523060a082015260c001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905262000820929160200162000f27565b6040516020818303038152906040528051906020012062000d0f565b92915050565b6040517f8948874900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff828116600483015260009182917f00000000000000000000000000000000000000000000000000000000000000001690638948874990602401602060405180830381865afa158015620008d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008fa919062000ed5565b905073ffffffffffffffffffffffffffffffffffffffff81166200094a576040517f3cfea2b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000801b837f0000000000000000000000000000000000000000000000000000000000000000837f000000000000000000000000000000000000000000000000000000000000000030604051620009a19062000d95565b73ffffffffffffffffffffffffffffffffffffffff95861681529385166020850152918416604084015283166060830152909116608082015260a0018190604051809103906000f5905080158015620009fe573d6000803e3d6000fd5b5060405173ffffffffffffffffffffffffffffffffffffffff8083168252919350908416907fcbd4e5d1362721c7fd10daaabb1a046344447d4798be55684b8a6b4fc1765d729060200160405180910390a250919050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000ad9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff83169063a694fc3a906024015b600060405180830381600087803b15801562000b4357600080fd5b505af115801562000b58573d6000803e3d6000fd5b505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000be3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517f7c6967dc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690637c6967dc9060240162000b28565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000cbb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517f2e17de780000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690632e17de789060240162000b28565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b16602182015260006035820181905260558201839052906200083c906075016040516020818303038152906040528051906020012090565b6136878062000f4983390190565b73ffffffffffffffffffffffffffffffffffffffff8116811462000dc657600080fd5b50565b60006020828403121562000ddc57600080fd5b813562000de98162000da3565b9392505050565b6000806040838503121562000e0457600080fd5b823562000e118162000da3565b9150602083013562000e238162000da3565b809150509250929050565b60008060006060848603121562000e4457600080fd5b833562000e518162000da3565b925060208401359150604084013562000e6a8162000da3565b809150509250925092565b6000806040838503121562000e8957600080fd5b823562000e968162000da3565b946020939093013593505050565b6000806040838503121562000eb857600080fd5b825162000ec58162000da3565b6020939093015192949293505050565b60006020828403121562000ee857600080fd5b815162000de98162000da3565b6000815160005b8181101562000f18576020818501810151868301520162000efc565b50600093019283525090919050565b600062000f4062000f39838662000ef5565b8462000ef5565b94935050505056fe6101606040523480156200001257600080fd5b50604051620036873803806200368783398101604081905262000035916200032f565b808585858262000045816200016a565b6200005085620001fd565b8181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000091573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000b79190620003af565b6000620000c5848262000480565b506001620000d4838262000480565b5060ff81166080524660a052620000ea6200027a565b60c052505050506001600160a01b0391821660e0525091821661010052811661012052600680546001600160a01b03191691841691821790556040519091506000907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350506001600160a01b031661014052506200071b915050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620001ab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620001d5919081019062000572565b604051602001620001e791906200062a565b6040516020818303038152906040529050919050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa1580156200023e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000268919081019062000572565b604051602001620001e7919062000671565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002ae91906200069d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b03811681146200032c57600080fd5b50565b600080600080600060a086880312156200034857600080fd5b8551620003558162000316565b6020870151909550620003688162000316565b60408701519094506200037b8162000316565b60608701519093506200038e8162000316565b6080870151909250620003a18162000316565b809150509295509295909350565b600060208284031215620003c257600080fd5b815160ff81168114620003d457600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200040657607f821691505b6020821081036200042757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200047b57600081815260208120601f850160051c81016020861015620004565750805b601f850160051c820191505b81811015620004775782815560010162000462565b5050505b505050565b81516001600160401b038111156200049c576200049c620003db565b620004b481620004ad8454620003f1565b846200042d565b602080601f831160018114620004ec5760008415620004d35750858301515b600019600386901b1c1916600185901b17855562000477565b600085815260208120601f198616915b828110156200051d57888601518255948401946001909101908401620004fc565b50858210156200053c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60005b83811015620005695781810151838201526020016200054f565b50506000910152565b6000602082840312156200058557600080fd5b81516001600160401b03808211156200059d57600080fd5b818401915084601f830112620005b257600080fd5b815181811115620005c757620005c7620003db565b604051601f8201601f19908116603f01168101908382118183101715620005f257620005f2620003db565b816040528281528760208487010111156200060c57600080fd5b6200061f8360208301602088016200054c565b979650505050505050565b7f455243343632362d577261707065642045756c65722000000000000000000000815260008251620006648160168501602087016200054c565b9190910160160192915050565b61776560f01b815260008251620006908160028501602087016200054c565b9190910160020192915050565b6000808354620006ad81620003f1565b60018281168015620006c85760018114620006de576200070f565b60ff19841687528215158302870194506200070f565b8760005260208060002060005b85811015620007065781548a820152908401908201620006eb565b50505082870194505b50929695505050505050565b60805160a05160c05160e051610100516101205161014051612e7e620008096000396000818161045b0152611e2601526000818161059401528181610738015281816107d10152818161089401528181611eb201528181611fee0152818161237401528181612423015281816124fb015281816125db01526129860152600081816106810152818161172301528181611c84015261231901526000818161040f01528181610fff0152818161116a015281816114c80152818161169f0152818161175001528181611cb101526122f701526000610e1c01526000610de7015260006103ce0152612e7e6000f3fe608060405234801561001057600080fd5b50600436106102d35760003560e01c80637c6967dc11610186578063ba087652116100e3578063d505accf11610097578063dd62ed3e11610071578063dd62ed3e146106a3578063e0bd6775146106ce578063ef8b30f7146106e157600080fd5b8063d505accf14610656578063d905777e14610669578063db661b711461067c57600080fd5b8063c6e6f592116100c8578063c6e6f5921461061d578063ce96cb7714610630578063d279c1911461064357600080fd5b8063ba0876521461060a578063c63d75b61461047d57600080fd5b806395d89b411161013a578063a9059cbb1161011f578063a9059cbb146105d1578063b3d7f6b9146105e4578063b460af94146105f757600080fd5b806395d89b41146105b6578063a694fc3a146105be57600080fd5b80638da5cb5b1161016b5780638da5cb5b1461055c57806394bf804d1461057c578063953d9cf11461058f57600080fd5b80637c6967dc146105295780637ecebe001461053c57600080fd5b8063313ce56711610234578063497f285d116101e857806364b87a70116101cd57806364b87a70146104d65780636e553f65146104f657806370a082311461050957600080fd5b8063497f285d146104b05780634cdad506146104c357600080fd5b806338d52e0f1161021957806338d52e0f1461040a5780633fc6df6e14610456578063402d267d1461047d57600080fd5b8063313ce567146103c95780633644e5151461040257600080fd5b806313af40351161028b578063228cb73311610270578063228cb7331461036f57806323b872dd146103a35780632e17de78146103b657600080fd5b806313af40351461035157806318160ddd1461036657600080fd5b806307a2d13a116102bc57806307a2d13a14610308578063095ea7b31461031b5780630a28a4771461033e57600080fd5b806301e1d114146102d857806306fdde03146102f3575b600080fd5b6102e06106f4565b6040519081526020015b60405180910390f35b6102fb61091b565b6040516102ea91906129ca565b6102e0610316366004612a36565b6109a9565b61032e610329366004612a74565b6109d6565b60405190151581526020016102ea565b6102e061034c366004612a36565b610a50565b61036461035f366004612aa0565b610a70565b005b6102e060025481565b610377610b67565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016102ea565b61032e6103b1366004612abd565b610b96565b6103646103c4366004612a36565b610cda565b6103f07f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102ea565b6102e0610de3565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ea565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102e061048b366004612aa0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b6103776104be366004612aa0565b610e3e565b6102e06104d1366004612a36565b610f63565b6007546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e0610504366004612afe565b610f6e565b6102e0610517366004612aa0565b60036020526000908152604090205481565b610364610537366004612aa0565b61108e565b6102e061054a366004612aa0565b60056020526000908152604090205481565b6006546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e061058a366004612afe565b611143565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102fb6111f9565b6103646105cc366004612a36565b611206565b61032e6105df366004612a74565b6112de565b6102e06105f2366004612a36565b611363565b6102e0610605366004612b2e565b611382565b6102e0610618366004612b2e565b6114ef565b6102e061062b366004612a36565b6116c6565b6102e061063e366004612aa0565b6116e6565b610377610651366004612aa0565b611807565b610364610664366004612b70565b611928565b6102e0610677366004612aa0565b611c47565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102e06106b1366004612be7565b600460209081526000928352604080842090915290825290205481565b6103646106dc366004612afe565b611d6a565b6102e06106ef366004612a36565b61203b565b6000806106ff612046565b90508015610866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b89190612c15565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663010ad6d16108008385612c5d565b6040518263ffffffff1660e01b815260040161081e91815260200190565b602060405180830381865afa15801561083b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085f9190612c15565b9250505090565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690633af9e669906024015b602060405180830381865afa1580156108f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109159190612c15565b91505090565b6000805461092890612c70565b80601f016020809104026020016040519081016040528092919081815260200182805461095490612c70565b80156109a15780601f10610976576101008083540402835291602001916109a1565b820191906000526020600020905b81548152906001019060200180831161098457829003601f168201915b505050505081565b60025460009080156109cd576109c86109c06106f4565b8490836120be565b6109cf565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610a3e9086815260200190565b60405180910390a35060015b92915050565b60025460009080156109cd576109c881610a686106f4565b8591906120dd565b60065473ffffffffffffffffffffffffffffffffffffffff163314610af6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a350565b6007546000908190610b8e9073ffffffffffffffffffffffffffffffffffffffff16610e3e565b915091509091565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c2a57610bf88382612cc3565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610c5f908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610cc79087815260200190565b60405180910390a3506001949350505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314610d5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d906024015b600060405180830381600087803b158015610dc857600080fd5b505af1158015610ddc573d6000803e3d6000fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610e1957610e1461210b565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b60008073ffffffffffffffffffffffffffffffffffffffff831615610f5e578273ffffffffffffffffffffffffffffffffffffffff1663d1af0c7d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecc9190612cd6565b6040517e8cc26200000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff841690628cc26290602401602060405180830381865afa158015610f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5b9190612c15565b90505b915091565b6000610a4a826109a9565b6000610f798361203b565b905080600003610fe5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401610aed565b61102773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330866121a5565b6110318282612264565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a83826122dd565b60065473ffffffffffffffffffffffffffffffffffffffff16331461110f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611118816123ea565b50600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600061114e83611363565b905061119273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846121a5565b61119c8284612264565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a81846122dd565b6001805461092890612c70565b60065473ffffffffffffffffffffffffffffffffffffffff163314611287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063a694fc3a90602401610dae565b336000908152600360205260408120805483919083906112ff908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610a3e9086815260200190565b60025460009080156109cd576109c861137a6106f4565b8490836120dd565b600061138d84610a50565b90503373ffffffffffffffffffffffffffffffffffffffff8316146114425773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146114405761140e8282612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b61144c84826124ca565b6114568282612729565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001684866127b7565b60003373ffffffffffffffffffffffffffffffffffffffff8316146115a45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146115a2576115708582612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6115ad84610f63565b905080600003611619576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401610aed565b61162381856124ca565b61162d8285612729565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001684836127b7565b60025460009080156109cd576109c8816116de6106f4565b8591906120be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611797573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bb9190612c15565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260036020526040812054919250906117ee906109a9565b90508082106117fd57806117ff565b815b949350505050565b600654600090819073ffffffffffffffffffffffffffffffffffffffff16331461188d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611895610b67565b600754604080517f3d18b912000000000000000000000000000000000000000000000000000000008152905193955091935073ffffffffffffffffffffffffffffffffffffffff1691633d18b9129160048082019260009290919082900301818387803b15801561190557600080fd5b505af1158015611919573d6000803e3d6000fd5b50505050610f5e828285612870565b42841015611992576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610aed565b6000600161199e610de3565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611af0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611b6b57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611bd1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610aed565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1c9190612c15565b90506000611d29826116c6565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040902054909150808210611d5f5780611d61565b815b95945050505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314611deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611df4816123ea565b6040517f4487d3df000000000000000000000000000000000000000000000000000000008152600481018390526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634487d3df906024016040805180830381865afa158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea59190612cf3565b90506000816000015190507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f569190612cd6565b73ffffffffffffffffffffffffffffffffffffffff1614611fa3576040517f63eea51600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691909117909155612035907f000000000000000000000000000000000000000000000000000000000000000016827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612897565b50505050565b6000610a4a826116c6565b60075460009073ffffffffffffffffffffffffffffffffffffffff1680156120ba576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8216906370a08231906024016108d4565b5090565b8282028115158415858304851417166120d657600080fd5b0492915050565b8282028115158415858304851417166120f557600080fd5b6001826001830304018115150290509392505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161213d9190612d72565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080610ddc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610aed565b80600260008282546122769190612c5d565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b61233e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000084612897565b6040517fe2bbb15800000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e2bbb158906044015b600060405180830381600087803b1580156123ce57600080fd5b505af11580156123e2573d6000803e3d6000fd5b505050505050565b60075473ffffffffffffffffffffffffffffffffffffffff1680156124c65761244b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016826000612897565b60008061245783610e3e565b915091508273ffffffffffffffffffffffffffffffffffffffff1663e9fad8ee6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156124a357600080fd5b505af11580156124b7573d6000803e3d6000fd5b50505050612035828286612870565b5050565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690633af9e66990602401602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190612c15565b90508281101561271a5760006125918285612cc3565b6040517f52eac8af0000000000000000000000000000000000000000000000000000000081526004810182905290915060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906352eac8af90602401602060405180830381865afa158015612622573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126469190612c15565b90506000612652612046565b905060006126608383612cc3565b9050600061267182600560646120dd565b61267b9085612c5d565b9050600081841061268c578161268e565b835b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905291925073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b1580156126fb57600080fd5b505af115801561270f573d6000803e3d6000fd5b505050505050505050505b6127248383612950565b505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805483929061275e908490612cc3565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016122d1565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610aed565b81156127245761272473ffffffffffffffffffffffffffffffffffffffff841682846127b7565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610aed565b6040517f441a3e7000000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063441a3e70906044016123b4565b600060208083528351808285015260005b818110156129f7578581018301518582016040015282016129db565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600060208284031215612a4857600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612a7157600080fd5b50565b60008060408385031215612a8757600080fd5b8235612a9281612a4f565b946020939093013593505050565b600060208284031215612ab257600080fd5b81356109cf81612a4f565b600080600060608486031215612ad257600080fd5b8335612add81612a4f565b92506020840135612aed81612a4f565b929592945050506040919091013590565b60008060408385031215612b1157600080fd5b823591506020830135612b2381612a4f565b809150509250929050565b600080600060608486031215612b4357600080fd5b833592506020840135612b5581612a4f565b91506040840135612b6581612a4f565b809150509250925092565b600080600080600080600060e0888a031215612b8b57600080fd5b8735612b9681612a4f565b96506020880135612ba681612a4f565b95506040880135945060608801359350608088013560ff81168114612bca57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215612bfa57600080fd5b8235612c0581612a4f565b91506020830135612b2381612a4f565b600060208284031215612c2757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610a4a57610a4a612c2e565b600181811c90821680612c8457607f821691505b602082108103612cbd577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b81810381811115610a4a57610a4a612c2e565b600060208284031215612ce857600080fd5b81516109cf81612a4f565b600060408284031215612d0557600080fd5b6040516040810181811067ffffffffffffffff82111715612d4f577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528251612d5d81612a4f565b81526020928301519281019290925250919050565b600080835481600182811c915080831680612d8e57607f831692505b60208084108203612dc6577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015612dda5760018114612e0d57612e3a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650612e3a565b60008a81526020902060005b86811015612e325781548b820152908501908301612e19565b505084890196505b50949897505050505050505056fea26469706673582212205b2ec9951b3f843925558c2e058c3dd16626e97babb9bf6e691e03b57940817164736f6c63430008100033a2646970667358221220081ef3243f788f78811b2742967facb5f95f625a0f11b38820bbf1c5bba274c164736f6c6343000810003300000000000000000000000027182842e098f60e3d576794a5bffb0777e025d30000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d3000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c43000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620000f15760003560e01c806380cfde3c1162000097578063adc9772e116200006e578063adc9772e146200024a578063af44af5b1462000261578063c2a672e01462000278578063db661b71146200028f57600080fd5b806380cfde3c14620001fb5780638da5cb5b1462000212578063abeccaa4146200023357600080fd5b80634953c78211620000cc5780634953c78214620001895780636353586b14620001cd5780637dc353e114620001e457600080fd5b806313af403514620000f65780633ac2a032146200010f5780633fc6df6e1462000161575b600080fd5b6200010d6200010736600462000dc9565b620002b7565b005b620001377f0000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d381565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001377f000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c4381565b620001a06200019a36600462000df0565b620003ae565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091520162000158565b620001a0620001de36600462000dc9565b620004d3565b6200010d620001f536600462000e2e565b62000550565b620001376200020c36600462000dc9565b62000662565b600054620001379073ffffffffffffffffffffffffffffffffffffffff1681565b620001376200024436600462000dc9565b62000842565b6200010d6200025b36600462000e75565b62000a56565b6200010d6200027236600462000df0565b62000b60565b6200010d6200028936600462000e75565b62000c38565b620001377f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d381565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200033e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008054819073ffffffffffffffffffffffffffffffffffffffff16331462000434576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fd279c19100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015285169063d279c1919060240160408051808303816000875af1158015620004a2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c8919062000ea4565b915091509250929050565b6000808273ffffffffffffffffffffffffffffffffffffffff1663228cb7336040518163ffffffff1660e01b81526004016040805180830381865afa15801562000521573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000547919062000ea4565b91509150915091565b60005473ffffffffffffffffffffffffffffffffffffffff163314620005d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fe0bd67750000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff828116602483015284169063e0bd677590604401600060405180830381600087803b1580156200064457600080fd5b505af115801562000659573d6000803e3d6000fd5b50505050505050565b60006200083c604051806020016200067a9062000d95565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f9091011660408190527f8948874900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015285917f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d3917f0000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d31690638948874990602401602060405180830381865afa1580156200075b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000781919062000ed5565b6040805173ffffffffffffffffffffffffffffffffffffffff948516602082015292841690830152821660608201527f000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c4390911660808201523060a082015260c001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905262000820929160200162000f27565b6040516020818303038152906040528051906020012062000d0f565b92915050565b6040517f8948874900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff828116600483015260009182917f0000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d31690638948874990602401602060405180830381865afa158015620008d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008fa919062000ed5565b905073ffffffffffffffffffffffffffffffffffffffff81166200094a576040517f3cfea2b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000801b837f00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d3837f000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c4330604051620009a19062000d95565b73ffffffffffffffffffffffffffffffffffffffff95861681529385166020850152918416604084015283166060830152909116608082015260a0018190604051809103906000f5905080158015620009fe573d6000803e3d6000fd5b5060405173ffffffffffffffffffffffffffffffffffffffff8083168252919350908416907fcbd4e5d1362721c7fd10daaabb1a046344447d4798be55684b8a6b4fc1765d729060200160405180910390a250919050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000ad9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff83169063a694fc3a906024015b600060405180830381600087803b15801562000b4357600080fd5b505af115801562000b58573d6000803e3d6000fd5b505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000be3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517f7c6967dc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690637c6967dc9060240162000b28565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000cbb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640162000335565b6040517f2e17de780000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690632e17de789060240162000b28565b6040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b16602182015260006035820181905260558201839052906200083c906075016040516020818303038152906040528051906020012090565b6136878062000f4983390190565b73ffffffffffffffffffffffffffffffffffffffff8116811462000dc657600080fd5b50565b60006020828403121562000ddc57600080fd5b813562000de98162000da3565b9392505050565b6000806040838503121562000e0457600080fd5b823562000e118162000da3565b9150602083013562000e238162000da3565b809150509250929050565b60008060006060848603121562000e4457600080fd5b833562000e518162000da3565b925060208401359150604084013562000e6a8162000da3565b809150509250925092565b6000806040838503121562000e8957600080fd5b823562000e968162000da3565b946020939093013593505050565b6000806040838503121562000eb857600080fd5b825162000ec58162000da3565b6020939093015192949293505050565b60006020828403121562000ee857600080fd5b815162000de98162000da3565b6000815160005b8181101562000f18576020818501810151868301520162000efc565b50600093019283525090919050565b600062000f4062000f39838662000ef5565b8462000ef5565b94935050505056fe6101606040523480156200001257600080fd5b50604051620036873803806200368783398101604081905262000035916200032f565b808585858262000045816200016a565b6200005085620001fd565b8181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000091573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000b79190620003af565b6000620000c5848262000480565b506001620000d4838262000480565b5060ff81166080524660a052620000ea6200027a565b60c052505050506001600160a01b0391821660e0525091821661010052811661012052600680546001600160a01b03191691841691821790556040519091506000907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350506001600160a01b031661014052506200071b915050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620001ab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620001d5919081019062000572565b604051602001620001e791906200062a565b6040516020818303038152906040529050919050565b6060816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa1580156200023e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000268919081019062000572565b604051602001620001e7919062000671565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002ae91906200069d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b03811681146200032c57600080fd5b50565b600080600080600060a086880312156200034857600080fd5b8551620003558162000316565b6020870151909550620003688162000316565b60408701519094506200037b8162000316565b60608701519093506200038e8162000316565b6080870151909250620003a18162000316565b809150509295509295909350565b600060208284031215620003c257600080fd5b815160ff81168114620003d457600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200040657607f821691505b6020821081036200042757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200047b57600081815260208120601f850160051c81016020861015620004565750805b601f850160051c820191505b81811015620004775782815560010162000462565b5050505b505050565b81516001600160401b038111156200049c576200049c620003db565b620004b481620004ad8454620003f1565b846200042d565b602080601f831160018114620004ec5760008415620004d35750858301515b600019600386901b1c1916600185901b17855562000477565b600085815260208120601f198616915b828110156200051d57888601518255948401946001909101908401620004fc565b50858210156200053c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60005b83811015620005695781810151838201526020016200054f565b50506000910152565b6000602082840312156200058557600080fd5b81516001600160401b03808211156200059d57600080fd5b818401915084601f830112620005b257600080fd5b815181811115620005c757620005c7620003db565b604051601f8201601f19908116603f01168101908382118183101715620005f257620005f2620003db565b816040528281528760208487010111156200060c57600080fd5b6200061f8360208301602088016200054c565b979650505050505050565b7f455243343632362d577261707065642045756c65722000000000000000000000815260008251620006648160168501602087016200054c565b9190910160160192915050565b61776560f01b815260008251620006908160028501602087016200054c565b9190910160020192915050565b6000808354620006ad81620003f1565b60018281168015620006c85760018114620006de576200070f565b60ff19841687528215158302870194506200070f565b8760005260208060002060005b85811015620007065781548a820152908401908201620006eb565b50505082870194505b50929695505050505050565b60805160a05160c05160e051610100516101205161014051612e7e620008096000396000818161045b0152611e2601526000818161059401528181610738015281816107d10152818161089401528181611eb201528181611fee0152818161237401528181612423015281816124fb015281816125db01526129860152600081816106810152818161172301528181611c84015261231901526000818161040f01528181610fff0152818161116a015281816114c80152818161169f0152818161175001528181611cb101526122f701526000610e1c01526000610de7015260006103ce0152612e7e6000f3fe608060405234801561001057600080fd5b50600436106102d35760003560e01c80637c6967dc11610186578063ba087652116100e3578063d505accf11610097578063dd62ed3e11610071578063dd62ed3e146106a3578063e0bd6775146106ce578063ef8b30f7146106e157600080fd5b8063d505accf14610656578063d905777e14610669578063db661b711461067c57600080fd5b8063c6e6f592116100c8578063c6e6f5921461061d578063ce96cb7714610630578063d279c1911461064357600080fd5b8063ba0876521461060a578063c63d75b61461047d57600080fd5b806395d89b411161013a578063a9059cbb1161011f578063a9059cbb146105d1578063b3d7f6b9146105e4578063b460af94146105f757600080fd5b806395d89b41146105b6578063a694fc3a146105be57600080fd5b80638da5cb5b1161016b5780638da5cb5b1461055c57806394bf804d1461057c578063953d9cf11461058f57600080fd5b80637c6967dc146105295780637ecebe001461053c57600080fd5b8063313ce56711610234578063497f285d116101e857806364b87a70116101cd57806364b87a70146104d65780636e553f65146104f657806370a082311461050957600080fd5b8063497f285d146104b05780634cdad506146104c357600080fd5b806338d52e0f1161021957806338d52e0f1461040a5780633fc6df6e14610456578063402d267d1461047d57600080fd5b8063313ce567146103c95780633644e5151461040257600080fd5b806313af40351161028b578063228cb73311610270578063228cb7331461036f57806323b872dd146103a35780632e17de78146103b657600080fd5b806313af40351461035157806318160ddd1461036657600080fd5b806307a2d13a116102bc57806307a2d13a14610308578063095ea7b31461031b5780630a28a4771461033e57600080fd5b806301e1d114146102d857806306fdde03146102f3575b600080fd5b6102e06106f4565b6040519081526020015b60405180910390f35b6102fb61091b565b6040516102ea91906129ca565b6102e0610316366004612a36565b6109a9565b61032e610329366004612a74565b6109d6565b60405190151581526020016102ea565b6102e061034c366004612a36565b610a50565b61036461035f366004612aa0565b610a70565b005b6102e060025481565b610377610b67565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016102ea565b61032e6103b1366004612abd565b610b96565b6103646103c4366004612a36565b610cda565b6103f07f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102ea565b6102e0610de3565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ea565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102e061048b366004612aa0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b6103776104be366004612aa0565b610e3e565b6102e06104d1366004612a36565b610f63565b6007546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e0610504366004612afe565b610f6e565b6102e0610517366004612aa0565b60036020526000908152604090205481565b610364610537366004612aa0565b61108e565b6102e061054a366004612aa0565b60056020526000908152604090205481565b6006546104319073ffffffffffffffffffffffffffffffffffffffff1681565b6102e061058a366004612afe565b611143565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102fb6111f9565b6103646105cc366004612a36565b611206565b61032e6105df366004612a74565b6112de565b6102e06105f2366004612a36565b611363565b6102e0610605366004612b2e565b611382565b6102e0610618366004612b2e565b6114ef565b6102e061062b366004612a36565b6116c6565b6102e061063e366004612aa0565b6116e6565b610377610651366004612aa0565b611807565b610364610664366004612b70565b611928565b6102e0610677366004612aa0565b611c47565b6104317f000000000000000000000000000000000000000000000000000000000000000081565b6102e06106b1366004612be7565b600460209081526000928352604080842090915290825290205481565b6103646106dc366004612afe565b611d6a565b6102e06106ef366004612a36565b61203b565b6000806106ff612046565b90508015610866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b89190612c15565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663010ad6d16108008385612c5d565b6040518263ffffffff1660e01b815260040161081e91815260200190565b602060405180830381865afa15801561083b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085f9190612c15565b9250505090565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690633af9e669906024015b602060405180830381865afa1580156108f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109159190612c15565b91505090565b6000805461092890612c70565b80601f016020809104026020016040519081016040528092919081815260200182805461095490612c70565b80156109a15780601f10610976576101008083540402835291602001916109a1565b820191906000526020600020905b81548152906001019060200180831161098457829003601f168201915b505050505081565b60025460009080156109cd576109c86109c06106f4565b8490836120be565b6109cf565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610a3e9086815260200190565b60405180910390a35060015b92915050565b60025460009080156109cd576109c881610a686106f4565b8591906120dd565b60065473ffffffffffffffffffffffffffffffffffffffff163314610af6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a350565b6007546000908190610b8e9073ffffffffffffffffffffffffffffffffffffffff16610e3e565b915091509091565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c2a57610bf88382612cc3565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610c5f908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610cc79087815260200190565b60405180910390a3506001949350505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314610d5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d906024015b600060405180830381600087803b158015610dc857600080fd5b505af1158015610ddc573d6000803e3d6000fd5b5050505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610e1957610e1461210b565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b60008073ffffffffffffffffffffffffffffffffffffffff831615610f5e578273ffffffffffffffffffffffffffffffffffffffff1663d1af0c7d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecc9190612cd6565b6040517e8cc26200000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff841690628cc26290602401602060405180830381865afa158015610f37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5b9190612c15565b90505b915091565b6000610a4a826109a9565b6000610f798361203b565b905080600003610fe5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401610aed565b61102773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330866121a5565b6110318282612264565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a83826122dd565b60065473ffffffffffffffffffffffffffffffffffffffff16331461110f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611118816123ea565b50600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600061114e83611363565b905061119273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330846121a5565b61119c8284612264565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610a4a81846122dd565b6001805461092890612c70565b60065473ffffffffffffffffffffffffffffffffffffffff163314611287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b6007546040517fa694fc3a0000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063a694fc3a90602401610dae565b336000908152600360205260408120805483919083906112ff908490612cc3565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610a3e9086815260200190565b60025460009080156109cd576109c861137a6106f4565b8490836120dd565b600061138d84610a50565b90503373ffffffffffffffffffffffffffffffffffffffff8316146114425773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146114405761140e8282612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b61144c84826124ca565b6114568282612729565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001684866127b7565b60003373ffffffffffffffffffffffffffffffffffffffff8316146115a45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146115a2576115708582612cc3565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6115ad84610f63565b905080600003611619576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401610aed565b61162381856124ca565b61162d8285612729565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46109cf73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001684836127b7565b60025460009080156109cd576109c8816116de6106f4565b8591906120be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611797573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bb9190612c15565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260036020526040812054919250906117ee906109a9565b90508082106117fd57806117ff565b815b949350505050565b600654600090819073ffffffffffffffffffffffffffffffffffffffff16331461188d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611895610b67565b600754604080517f3d18b912000000000000000000000000000000000000000000000000000000008152905193955091935073ffffffffffffffffffffffffffffffffffffffff1691633d18b9129160048082019260009290919082900301818387803b15801561190557600080fd5b505af1158015611919573d6000803e3d6000fd5b50505050610f5e828285612870565b42841015611992576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610aed565b6000600161199e610de3565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611af0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611b6b57508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611bd1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610aed565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1c9190612c15565b90506000611d29826116c6565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260036020526040902054909150808210611d5f5780611d61565b815b95945050505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314611deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610aed565b611df4816123ea565b6040517f4487d3df000000000000000000000000000000000000000000000000000000008152600481018390526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634487d3df906024016040805180830381865afa158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea59190612cf3565b90506000816000015190507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff166372f702f36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f569190612cd6565b73ffffffffffffffffffffffffffffffffffffffff1614611fa3576040517f63eea51600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691909117909155612035907f000000000000000000000000000000000000000000000000000000000000000016827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612897565b50505050565b6000610a4a826116c6565b60075460009073ffffffffffffffffffffffffffffffffffffffff1680156120ba576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8216906370a08231906024016108d4565b5090565b8282028115158415858304851417166120d657600080fd5b0492915050565b8282028115158415858304851417166120f557600080fd5b6001826001830304018115150290509392505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161213d9190612d72565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080610ddc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610aed565b80600260008282546122769190612c5d565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b61233e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000084612897565b6040517fe2bbb15800000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e2bbb158906044015b600060405180830381600087803b1580156123ce57600080fd5b505af11580156123e2573d6000803e3d6000fd5b505050505050565b60075473ffffffffffffffffffffffffffffffffffffffff1680156124c65761244b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016826000612897565b60008061245783610e3e565b915091508273ffffffffffffffffffffffffffffffffffffffff1663e9fad8ee6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156124a357600080fd5b505af11580156124b7573d6000803e3d6000fd5b50505050612035828286612870565b5050565b6040517f3af9e6690000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690633af9e66990602401602060405180830381865afa158015612557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257b9190612c15565b90508281101561271a5760006125918285612cc3565b6040517f52eac8af0000000000000000000000000000000000000000000000000000000081526004810182905290915060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906352eac8af90602401602060405180830381865afa158015612622573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126469190612c15565b90506000612652612046565b905060006126608383612cc3565b9050600061267182600560646120dd565b61267b9085612c5d565b9050600081841061268c578161268e565b835b6007546040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810183905291925073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b1580156126fb57600080fd5b505af115801561270f573d6000803e3d6000fd5b505050505050505050505b6127248383612950565b505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805483929061275e908490612cc3565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016122d1565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610aed565b81156127245761272473ffffffffffffffffffffffffffffffffffffffff841682846127b7565b60006040517f095ea7b3000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080612035576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401610aed565b6040517f441a3e7000000000000000000000000000000000000000000000000000000000815260006004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063441a3e70906044016123b4565b600060208083528351808285015260005b818110156129f7578581018301518582016040015282016129db565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600060208284031215612a4857600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114612a7157600080fd5b50565b60008060408385031215612a8757600080fd5b8235612a9281612a4f565b946020939093013593505050565b600060208284031215612ab257600080fd5b81356109cf81612a4f565b600080600060608486031215612ad257600080fd5b8335612add81612a4f565b92506020840135612aed81612a4f565b929592945050506040919091013590565b60008060408385031215612b1157600080fd5b823591506020830135612b2381612a4f565b809150509250929050565b600080600060608486031215612b4357600080fd5b833592506020840135612b5581612a4f565b91506040840135612b6581612a4f565b809150509250925092565b600080600080600080600060e0888a031215612b8b57600080fd5b8735612b9681612a4f565b96506020880135612ba681612a4f565b95506040880135945060608801359350608088013560ff81168114612bca57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215612bfa57600080fd5b8235612c0581612a4f565b91506020830135612b2381612a4f565b600060208284031215612c2757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610a4a57610a4a612c2e565b600181811c90821680612c8457607f821691505b602082108103612cbd577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b81810381811115610a4a57610a4a612c2e565b600060208284031215612ce857600080fd5b81516109cf81612a4f565b600060408284031215612d0557600080fd5b6040516040810181811067ffffffffffffffff82111715612d4f577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528251612d5d81612a4f565b81526020928301519281019290925250919050565b600080835481600182811c915080831680612d8e57607f831692505b60208084108203612dc6577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b818015612dda5760018114612e0d57612e3a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650612e3a565b60008a81526020902060005b86811015612e325781548b820152908501908301612e19565b505084890196505b50949897505050505050505056fea26469706673582212205b2ec9951b3f843925558c2e058c3dd16626e97babb9bf6e691e03b57940817164736f6c63430008100033a2646970667358221220081ef3243f788f78811b2742967facb5f95f625a0f11b38820bbf1c5bba274c164736f6c63430008100033

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

00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d30000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d3000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c43000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4

-----Decoded View---------------
Arg [0] : euler_ (address): 0x27182842E098f60e3D576794A5bFFb0777E025d3
Arg [1] : markets_ (address): 0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3
Arg [2] : rewardsDistribution_ (address): 0xA9839D52E964d0ed0d6D546c27D2248Fac610c43
Arg [3] : owner_ (address): 0xEC864BE26084ba3bbF3cAAcF8F6961A9263319C4

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d3
Arg [1] : 0000000000000000000000003520d5a913427e6f0d6a83e07ccd4a4da316e4d3
Arg [2] : 000000000000000000000000a9839d52e964d0ed0d6d546c27d2248fac610c43
Arg [3] : 000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4


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  ]
[ 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.