false
false
100

Contract Address Details

0xE9c057D6D4EDC8935c931Ec307DF225A1C2D04Ff

Contract Name
MagicKlpRewardHandler
Creator
0xfb3485–a80ee3 at 0xebda1d–9e59df
Balance
0 KAVA ( )
Tokens
Fetching tokens...
Transactions
1 Transactions
Transfers
0 Transfers
Gas Used
31,167
Last Balance Update
11603047
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
MagicKlpRewardHandler




Optimization enabled
true
Compiler version
v0.8.20+commit.a1b79de6




Optimization runs
800
EVM Version
paris




Verified at
2023-09-21T03:09:28.377270Z

src/periphery/MagicKlpRewardHandler.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "BoringSolidity/libraries/BoringERC20.sol";
import "BoringSolidity/BoringOwnable.sol";
import {MagicGlpData} from "tokens/MagicGlp.sol";

interface IKlpRewardHandler {
    function handleRewards(bool _shouldConvertWethToEth, bool _shouldAddIntoKLP) external;
}

contract MagicKlpRewardHandlerDataV1 is MagicGlpData {
    IKlpRewardHandler public rewardRouter;
    IERC20[] public rewardTokens;
}

contract MagicKlpRewardHandler is MagicKlpRewardHandlerDataV1 {
    using BoringERC20 for IERC20;

    event LogHarvest();
    event LogDistributeRewards(uint256 amount);
    event LogRewardRouterChanged(IKlpRewardHandler indexed previous, IKlpRewardHandler indexed current);
    event LogRewardTokensChanged(IERC20[] previous, IERC20[] current);

    function harvest() external onlyStrategyExecutor {
        rewardRouter.handleRewards(false, false);

        for (uint256 i = 0; i < rewardTokens.length; i++) {
            IERC20 rewardToken = rewardTokens[i];
            uint256 balance = rewardToken.balanceOf(address(this));
            if (balance > 0) {
                rewardToken.safeTransfer(msg.sender, balance);
            }
        }

        emit LogHarvest();
    }

    function distributeRewards(uint256 amount) external onlyStrategyExecutor {
        _asset.transferFrom(msg.sender, address(this), amount);
        _totalAssets += amount;

        emit LogDistributeRewards(amount);
    }

    function setRewardTokens(IERC20[] memory _rewardTokens) external onlyOwner {
        emit LogRewardTokensChanged(rewardTokens, _rewardTokens);
        rewardTokens = _rewardTokens;
    }

    function setRewardRouter(IKlpRewardHandler _rewardRouter) external onlyOwner {
        emit LogRewardRouterChanged(rewardRouter, _rewardRouter);
        rewardRouter = _rewardRouter;
    }

    function skimAssets() external onlyOwner returns (uint256 amount) {
        amount = _asset.balanceOf(address(this)) - _totalAssets;

        if (amount > 0) {
            _asset.transfer(msg.sender, amount);
        }
    }
}
        

lib/BoringSolidity/contracts/interfaces/IERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    // transfer and tranferFrom have been removed, because they don't work on all tokens (some aren't ERC20 complaint).
    // By removing them you can't accidentally use them.
    // name, symbol and decimals have been removed, because they are optional and sometimes wrongly implemented (MKR).
    // Use BoringERC20 with `using BoringERC20 for IERC20` and call `safeTransfer`, `safeTransferFrom`, etc instead.
    function totalSupply() external view returns (uint256);

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

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

interface IStrictERC20 {
    // This is the strict ERC20 interface. Don't use this, certainly not if you don't control the ERC20 token you're calling.
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256 balance);
    function transfer(address _to, uint256 _value) external returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
    function approve(address _spender, uint256 _value) external returns (bool success);
    function allowance(address _owner, address _spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

lib/BoringSolidity/contracts/BoringOwnable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Simplified by BoringCrypto

contract BoringOwnableData {
    address public owner;
    address public pendingOwner;
}

contract BoringOwnable is BoringOwnableData {
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /// @notice `owner` defaults to msg.sender on construction.
    constructor() {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
    /// Can only be invoked by the current `owner`.
    /// @param newOwner Address of the new owner.
    /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
    /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
    function transferOwnership(
        address newOwner,
        bool direct,
        bool renounce
    ) public onlyOwner {
        if (direct) {
            // Checks
            require(newOwner != address(0) || renounce, "Ownable: zero address");

            // Effects
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            pendingOwner = address(0);
        } else {
            // Effects
            pendingOwner = newOwner;
        }
    }

    /// @notice Needs to be called by `pendingOwner` to claim ownership.
    function claimOwnership() public {
        address _pendingOwner = pendingOwner;

        // Checks
        require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");

        // Effects
        emit OwnershipTransferred(owner, _pendingOwner);
        owner = _pendingOwner;
        pendingOwner = address(0);
    }

    /// @notice Only allows the `owner` to execute the function.
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }
}
          

lib/BoringSolidity/contracts/Domain.sol

// SPDX-License-Identifier: MIT
// Based on code and smartness by Ross Campbell and Keno
// Uses immutable to store the domain separator to reduce gas usage
// If the chain id changes due to a fork, the forked chain will calculate on the fly.
pragma solidity ^0.8.0;

// solhint-disable no-inline-assembly

contract Domain {
    bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
    // See https://eips.ethereum.org/EIPS/eip-191
    string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01";

    // solhint-disable var-name-mixedcase
    bytes32 private immutable _DOMAIN_SEPARATOR;
    uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;

    /// @dev Calculate the DOMAIN_SEPARATOR
    function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_SEPARATOR_SIGNATURE_HASH, chainId, address(this)));
    }

    constructor() {
        _DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = block.chainid);
    }

    /// @dev Return the DOMAIN_SEPARATOR
    // It's named internal to allow making it public from the contract that uses it by creating a simple view function
    // with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.
    // solhint-disable-next-line func-name-mixedcase
    function _domainSeparator() internal view returns (bytes32) {
        return block.chainid == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid);
    }

    function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {
        digest = keccak256(abi.encodePacked(EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA, _domainSeparator(), dataHash));
    }
}
          

lib/BoringSolidity/contracts/ERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IERC20.sol";
import "./Domain.sol";

// solhint-disable no-inline-assembly
// solhint-disable not-rely-on-time

// Data part taken out for building of contracts that receive delegate calls
contract ERC20Data {
    /// @notice owner > balance mapping.
    mapping(address => uint256) public balanceOf;
    /// @notice owner > spender > allowance mapping.
    mapping(address => mapping(address => uint256)) public allowance;
    /// @notice owner > nonce mapping. Used in `permit`.
    mapping(address => uint256) public nonces;
}

abstract contract ERC20 is IERC20, Domain {
    /// @notice owner > balance mapping.
    mapping(address => uint256) public override balanceOf;
    /// @notice owner > spender > allowance mapping.
    mapping(address => mapping(address => uint256)) public override allowance;
    /// @notice owner > nonce mapping. Used in `permit`.
    mapping(address => uint256) public nonces;

    /// @notice Transfers `amount` tokens from `msg.sender` to `to`.
    /// @param to The address to move the tokens.
    /// @param amount of the tokens to move.
    /// @return (bool) Returns True if succeeded.
    function transfer(address to, uint256 amount) public returns (bool) {
        // If `amount` is 0, or `msg.sender` is `to` nothing happens
        if (amount != 0 || msg.sender == to) {
            uint256 srcBalance = balanceOf[msg.sender];
            require(srcBalance >= amount, "ERC20: balance too low");
            if (msg.sender != to) {
                require(to != address(0), "ERC20: no zero address"); // Moved down so low balance calls safe some gas

                balanceOf[msg.sender] = srcBalance - amount; // Underflow is checked
                balanceOf[to] += amount;
            }
        }
        emit Transfer(msg.sender, to, amount);
        return true;
    }

    /// @notice Transfers `amount` tokens from `from` to `to`. Caller needs approval for `from`.
    /// @param from Address to draw tokens from.
    /// @param to The address to move the tokens.
    /// @param amount The token amount to move.
    /// @return (bool) Returns True if succeeded.
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        // If `amount` is 0, or `from` is `to` nothing happens
        if (amount != 0) {
            uint256 srcBalance = balanceOf[from];
            require(srcBalance >= amount, "ERC20: balance too low");

            if (from != to) {
                uint256 spenderAllowance = allowance[from][msg.sender];
                // If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).
                if (spenderAllowance != type(uint256).max) {
                    require(spenderAllowance >= amount, "ERC20: allowance too low");
                    allowance[from][msg.sender] = spenderAllowance - amount; // Underflow is checked
                }
                require(to != address(0), "ERC20: no zero address"); // Moved down so other failed calls safe some gas

                balanceOf[from] = srcBalance - amount; // Underflow is checked
                balanceOf[to] += amount;
            }
        }
        emit Transfer(from, to, amount);
        return true;
    }

    /// @notice Approves `amount` from sender to be spend by `spender`.
    /// @param spender Address of the party that can draw from msg.sender's account.
    /// @param amount The maximum collective amount that `spender` can draw.
    /// @return (bool) Returns True if approved.
    function approve(address spender, uint256 amount) public override returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return _domainSeparator();
    }

    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /// @notice Approves `value` from `owner_` to be spend by `spender`.
    /// @param owner_ Address of the owner.
    /// @param spender The address of the spender that gets approved to draw from `owner_`.
    /// @param value The maximum collective amount that `spender` can draw.
    /// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).
    function permit(
        address owner_,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external override {
        require(owner_ != address(0), "ERC20: Owner cannot be 0");
        require(block.timestamp < deadline, "ERC20: Expired");
        require(
            ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) ==
                owner_,
            "ERC20: Invalid Signature"
        );
        allowance[owner_][spender] = value;
        emit Approval(owner_, spender, value);
    }
}

contract ERC20WithSupply is IERC20, ERC20 {
    uint256 public override totalSupply;

    function _mint(address user, uint256 amount) internal {
        uint256 newTotalSupply = totalSupply + amount;
        require(newTotalSupply >= totalSupply, "Mint overflow");
        totalSupply = newTotalSupply;
        balanceOf[user] += amount;
        emit Transfer(address(0), user, amount);
    }

    function _burn(address user, uint256 amount) internal {
        require(balanceOf[user] >= amount, "Burn too much");
        totalSupply -= amount;
        balanceOf[user] -= amount;
        emit Transfer(user, address(0), amount);
    }
}
          

lib/BoringSolidity/contracts/libraries/BoringERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../interfaces/IERC20.sol";

// solhint-disable avoid-low-level-calls

library BoringERC20 {
    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
    bytes4 private constant SIG_NAME = 0x06fdde03; // name()
    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
    bytes4 private constant SIG_BALANCE_OF = 0x70a08231; // balanceOf(address)
    bytes4 private constant SIG_TOTALSUPPLY = 0x18160ddd; // balanceOf(address)
    bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
    bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)

    function returnDataToString(bytes memory data) internal pure returns (string memory) {
        if (data.length >= 64) {
            return abi.decode(data, (string));
        } else if (data.length == 32) {
            uint8 i = 0;
            while (i < 32 && data[i] != 0) {
                i++;
            }
            bytes memory bytesArray = new bytes(i);
            for (i = 0; i < 32 && data[i] != 0; i++) {
                bytesArray[i] = data[i];
            }
            return string(bytesArray);
        } else {
            return "???";
        }
    }

    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token symbol.
    function safeSymbol(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token name.
    function safeName(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
    /// @param token The address of the ERC-20 token contract.
    /// @return (uint8) Token decimals.
    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }

    /// @notice Provides a gas-optimized balance check to avoid a redundant extcodesize check in addition to the returndatasize check.
    /// @param token The address of the ERC-20 token.
    /// @param to The address of the user to check.
    /// @return amount The token amount.
    function safeBalanceOf(IERC20 token, address to) internal view returns (uint256 amount) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_BALANCE_OF, to));
        require(success && data.length >= 32, "BoringERC20: BalanceOf failed");
        amount = abi.decode(data, (uint256));
    }

    /// @notice Provides a gas-optimized totalSupply to avoid a redundant extcodesize check in addition to the returndatasize check.
    /// @param token The address of the ERC-20 token.
    /// @return totalSupply The token totalSupply.
    function safeTotalSupply(IERC20 token) internal view returns (uint256 totalSupply) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_TOTALSUPPLY));
        require(success && data.length >= 32, "BoringERC20: totalSupply failed");
        totalSupply = abi.decode(data, (uint256));
    }

    /// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
    /// Reverts on a failed transfer.
    /// @param token The address of the ERC-20 token.
    /// @param to Transfer tokens to.
    /// @param amount The token amount.
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
    }

    /// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
    /// Reverts on a failed transfer.
    /// @param token The address of the ERC-20 token.
    /// @param from Transfer tokens from.
    /// @param to Transfer tokens to.
    /// @param amount The token amount.
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
    }
}
          

lib/solmate/src/utils/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 MAX_UINT256 = 2**256 - 1;

    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) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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))
        }
    }
}
          

src/interfaces/IERC4626.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "BoringSolidity/interfaces/IERC20.sol";

/// @notice Tokenized Vaults with a single underlying EIP-20 token.
interface IERC4626 {
    function decimals() external view returns (uint8 decimals);
    
    /// @notice The address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    function asset() external view returns (IERC20 assetTokenAddress);

    /// @notice Total amount of the underlying asset that is “managed” by Vault.
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @notice The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @notice The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @notice Maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @notice Mints shares Vault shares to receiver by depositing exactly assets of underlying tokens.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /// @notice Maximum amount of shares that can be minted from the Vault for the receiver, through a mint call.
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @notice Mints exactly shares Vault shares to receiver by depositing assets of underlying tokens.
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /// @notice Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /// @notice Maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    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);
}
          

src/tokens/ERC4626.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
import {ERC20, ERC20WithSupply} from "BoringSolidity/ERC20.sol";
import {BoringERC20} from "BoringSolidity/libraries/BoringERC20.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {IERC4626} from "interfaces/IERC4626.sol";

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

    error ErrZeroAssets();
    error ErrZeroShares();

    string public name;
    string public symbol;
    ERC20 public _asset;
    uint256 internal _totalAssets;

    function asset() external view returns (IERC20) {
        return _asset;
    }

    function decimals() external view returns (uint8) {
        return _asset.safeDecimals();
    }

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        if ((shares = previewDeposit(assets)) == 0) {
            revert ErrZeroShares();
        }

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

        unchecked {
            _totalAssets += 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);

        unchecked {
            _totalAssets += 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);

        unchecked {
            _totalAssets -= 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.
        if ((assets = previewRedeem(shares)) == 0) {
            revert ErrZeroAssets();
        }

        _beforeWithdraw(assets, shares);
        _burn(owner, shares);
        emit Withdraw(msg.sender, receiver, owner, assets, shares);
        _asset.safeTransfer(receiver, assets);

        unchecked {
            _totalAssets -= assets;
        }
    }

    function totalAssets() public view virtual returns (uint256) {
        return _totalAssets;
    }

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        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;
        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

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

    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];
    }

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

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

src/tokens/MagicGlp.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "BoringSolidity/BoringOwnable.sol";
import "./ERC4626.sol";

contract MagicGlpData is ERC4626, BoringOwnable {
    error ErrNotStrategyExecutor(address);

    address public rewardHandler;
    mapping(address => bool) public strategyExecutors;

    modifier onlyStrategyExecutor() {
        if (msg.sender != owner && !strategyExecutors[msg.sender]) {
            revert ErrNotStrategyExecutor(msg.sender);
        }
        _;
    }
}

/// @dev Vault version of the GLP Wrapper, auto compound yield increase shares value
contract MagicGlp is MagicGlpData {
    event LogRewardHandlerChanged(address indexed previous, address indexed current);
    event LogStrategyExecutorChanged(address indexed executor, bool allowed);
    event LogStakedGlpChanged(ERC20 indexed previous, ERC20 indexed current);

    constructor(
        ERC20 __asset,
        string memory _name,
        string memory _symbol
    ) {
        _asset = __asset;
        name = _name;
        symbol = _symbol;
    }

    function setStrategyExecutor(address executor, bool value) external onlyOwner {
        strategyExecutors[executor] = value;
        emit LogStrategyExecutorChanged(executor, value);
    }

    function setRewardHandler(address _rewardHandler) external onlyOwner {
        emit LogRewardHandlerChanged(rewardHandler, _rewardHandler);
        rewardHandler = _rewardHandler;
    }

    function setStakedGlp(ERC20 _sGlp) external onlyOwner {
        emit LogStakedGlpChanged(_asset, _sGlp);
        _asset = _sGlp;
    }

    // Forward unknown function calls to the reward handler.
    fallback() external {
        _delegate(rewardHandler);
    }

    /**
     * From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol
     *
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) private {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}
          

lib/BoringSolidity/contracts/interfaces/IERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    // transfer and tranferFrom have been removed, because they don't work on all tokens (some aren't ERC20 complaint).
    // By removing them you can't accidentally use them.
    // name, symbol and decimals have been removed, because they are optional and sometimes wrongly implemented (MKR).
    // Use BoringERC20 with `using BoringERC20 for IERC20` and call `safeTransfer`, `safeTransferFrom`, etc instead.
    function totalSupply() external view returns (uint256);

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

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

interface IStrictERC20 {
    // This is the strict ERC20 interface. Don't use this, certainly not if you don't control the ERC20 token you're calling.
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256 balance);
    function transfer(address _to, uint256 _value) external returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
    function approve(address _spender, uint256 _value) external returns (bool success);
    function allowance(address _owner, address _spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice EIP 2612
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

lib/BoringSolidity/contracts/BoringOwnable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Simplified by BoringCrypto

contract BoringOwnableData {
    address public owner;
    address public pendingOwner;
}

contract BoringOwnable is BoringOwnableData {
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /// @notice `owner` defaults to msg.sender on construction.
    constructor() {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
    /// Can only be invoked by the current `owner`.
    /// @param newOwner Address of the new owner.
    /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
    /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
    function transferOwnership(
        address newOwner,
        bool direct,
        bool renounce
    ) public onlyOwner {
        if (direct) {
            // Checks
            require(newOwner != address(0) || renounce, "Ownable: zero address");

            // Effects
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            pendingOwner = address(0);
        } else {
            // Effects
            pendingOwner = newOwner;
        }
    }

    /// @notice Needs to be called by `pendingOwner` to claim ownership.
    function claimOwnership() public {
        address _pendingOwner = pendingOwner;

        // Checks
        require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");

        // Effects
        emit OwnershipTransferred(owner, _pendingOwner);
        owner = _pendingOwner;
        pendingOwner = address(0);
    }

    /// @notice Only allows the `owner` to execute the function.
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }
}
          

lib/BoringSolidity/contracts/Domain.sol

// SPDX-License-Identifier: MIT
// Based on code and smartness by Ross Campbell and Keno
// Uses immutable to store the domain separator to reduce gas usage
// If the chain id changes due to a fork, the forked chain will calculate on the fly.
pragma solidity ^0.8.0;

// solhint-disable no-inline-assembly

contract Domain {
    bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
    // See https://eips.ethereum.org/EIPS/eip-191
    string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01";

    // solhint-disable var-name-mixedcase
    bytes32 private immutable _DOMAIN_SEPARATOR;
    uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;

    /// @dev Calculate the DOMAIN_SEPARATOR
    function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {
        return keccak256(abi.encode(DOMAIN_SEPARATOR_SIGNATURE_HASH, chainId, address(this)));
    }

    constructor() {
        _DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = block.chainid);
    }

    /// @dev Return the DOMAIN_SEPARATOR
    // It's named internal to allow making it public from the contract that uses it by creating a simple view function
    // with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.
    // solhint-disable-next-line func-name-mixedcase
    function _domainSeparator() internal view returns (bytes32) {
        return block.chainid == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid);
    }

    function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {
        digest = keccak256(abi.encodePacked(EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA, _domainSeparator(), dataHash));
    }
}
          

lib/BoringSolidity/contracts/ERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interfaces/IERC20.sol";
import "./Domain.sol";

// solhint-disable no-inline-assembly
// solhint-disable not-rely-on-time

// Data part taken out for building of contracts that receive delegate calls
contract ERC20Data {
    /// @notice owner > balance mapping.
    mapping(address => uint256) public balanceOf;
    /// @notice owner > spender > allowance mapping.
    mapping(address => mapping(address => uint256)) public allowance;
    /// @notice owner > nonce mapping. Used in `permit`.
    mapping(address => uint256) public nonces;
}

abstract contract ERC20 is IERC20, Domain {
    /// @notice owner > balance mapping.
    mapping(address => uint256) public override balanceOf;
    /// @notice owner > spender > allowance mapping.
    mapping(address => mapping(address => uint256)) public override allowance;
    /// @notice owner > nonce mapping. Used in `permit`.
    mapping(address => uint256) public nonces;

    /// @notice Transfers `amount` tokens from `msg.sender` to `to`.
    /// @param to The address to move the tokens.
    /// @param amount of the tokens to move.
    /// @return (bool) Returns True if succeeded.
    function transfer(address to, uint256 amount) public returns (bool) {
        // If `amount` is 0, or `msg.sender` is `to` nothing happens
        if (amount != 0 || msg.sender == to) {
            uint256 srcBalance = balanceOf[msg.sender];
            require(srcBalance >= amount, "ERC20: balance too low");
            if (msg.sender != to) {
                require(to != address(0), "ERC20: no zero address"); // Moved down so low balance calls safe some gas

                balanceOf[msg.sender] = srcBalance - amount; // Underflow is checked
                balanceOf[to] += amount;
            }
        }
        emit Transfer(msg.sender, to, amount);
        return true;
    }

    /// @notice Transfers `amount` tokens from `from` to `to`. Caller needs approval for `from`.
    /// @param from Address to draw tokens from.
    /// @param to The address to move the tokens.
    /// @param amount The token amount to move.
    /// @return (bool) Returns True if succeeded.
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        // If `amount` is 0, or `from` is `to` nothing happens
        if (amount != 0) {
            uint256 srcBalance = balanceOf[from];
            require(srcBalance >= amount, "ERC20: balance too low");

            if (from != to) {
                uint256 spenderAllowance = allowance[from][msg.sender];
                // If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).
                if (spenderAllowance != type(uint256).max) {
                    require(spenderAllowance >= amount, "ERC20: allowance too low");
                    allowance[from][msg.sender] = spenderAllowance - amount; // Underflow is checked
                }
                require(to != address(0), "ERC20: no zero address"); // Moved down so other failed calls safe some gas

                balanceOf[from] = srcBalance - amount; // Underflow is checked
                balanceOf[to] += amount;
            }
        }
        emit Transfer(from, to, amount);
        return true;
    }

    /// @notice Approves `amount` from sender to be spend by `spender`.
    /// @param spender Address of the party that can draw from msg.sender's account.
    /// @param amount The maximum collective amount that `spender` can draw.
    /// @return (bool) Returns True if approved.
    function approve(address spender, uint256 amount) public override returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return _domainSeparator();
    }

    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /// @notice Approves `value` from `owner_` to be spend by `spender`.
    /// @param owner_ Address of the owner.
    /// @param spender The address of the spender that gets approved to draw from `owner_`.
    /// @param value The maximum collective amount that `spender` can draw.
    /// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).
    function permit(
        address owner_,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external override {
        require(owner_ != address(0), "ERC20: Owner cannot be 0");
        require(block.timestamp < deadline, "ERC20: Expired");
        require(
            ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) ==
                owner_,
            "ERC20: Invalid Signature"
        );
        allowance[owner_][spender] = value;
        emit Approval(owner_, spender, value);
    }
}

contract ERC20WithSupply is IERC20, ERC20 {
    uint256 public override totalSupply;

    function _mint(address user, uint256 amount) internal {
        uint256 newTotalSupply = totalSupply + amount;
        require(newTotalSupply >= totalSupply, "Mint overflow");
        totalSupply = newTotalSupply;
        balanceOf[user] += amount;
        emit Transfer(address(0), user, amount);
    }

    function _burn(address user, uint256 amount) internal {
        require(balanceOf[user] >= amount, "Burn too much");
        totalSupply -= amount;
        balanceOf[user] -= amount;
        emit Transfer(user, address(0), amount);
    }
}
          

lib/BoringSolidity/contracts/libraries/BoringERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../interfaces/IERC20.sol";

// solhint-disable avoid-low-level-calls

library BoringERC20 {
    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
    bytes4 private constant SIG_NAME = 0x06fdde03; // name()
    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
    bytes4 private constant SIG_BALANCE_OF = 0x70a08231; // balanceOf(address)
    bytes4 private constant SIG_TOTALSUPPLY = 0x18160ddd; // balanceOf(address)
    bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
    bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)

    function returnDataToString(bytes memory data) internal pure returns (string memory) {
        if (data.length >= 64) {
            return abi.decode(data, (string));
        } else if (data.length == 32) {
            uint8 i = 0;
            while (i < 32 && data[i] != 0) {
                i++;
            }
            bytes memory bytesArray = new bytes(i);
            for (i = 0; i < 32 && data[i] != 0; i++) {
                bytesArray[i] = data[i];
            }
            return string(bytesArray);
        } else {
            return "???";
        }
    }

    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token symbol.
    function safeSymbol(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token name.
    function safeName(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
    /// @param token The address of the ERC-20 token contract.
    /// @return (uint8) Token decimals.
    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }

    /// @notice Provides a gas-optimized balance check to avoid a redundant extcodesize check in addition to the returndatasize check.
    /// @param token The address of the ERC-20 token.
    /// @param to The address of the user to check.
    /// @return amount The token amount.
    function safeBalanceOf(IERC20 token, address to) internal view returns (uint256 amount) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_BALANCE_OF, to));
        require(success && data.length >= 32, "BoringERC20: BalanceOf failed");
        amount = abi.decode(data, (uint256));
    }

    /// @notice Provides a gas-optimized totalSupply to avoid a redundant extcodesize check in addition to the returndatasize check.
    /// @param token The address of the ERC-20 token.
    /// @return totalSupply The token totalSupply.
    function safeTotalSupply(IERC20 token) internal view returns (uint256 totalSupply) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_TOTALSUPPLY));
        require(success && data.length >= 32, "BoringERC20: totalSupply failed");
        totalSupply = abi.decode(data, (uint256));
    }

    /// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
    /// Reverts on a failed transfer.
    /// @param token The address of the ERC-20 token.
    /// @param to Transfer tokens to.
    /// @param amount The token amount.
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
    }

    /// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
    /// Reverts on a failed transfer.
    /// @param token The address of the ERC-20 token.
    /// @param from Transfer tokens from.
    /// @param to Transfer tokens to.
    /// @param amount The token amount.
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
    }
}
          

lib/solmate/src/utils/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 MAX_UINT256 = 2**256 - 1;

    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) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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) {
        /// @solidity memory-safe-assembly
        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))
        }
    }
}
          

src/interfaces/IERC4626.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "BoringSolidity/interfaces/IERC20.sol";

/// @notice Tokenized Vaults with a single underlying EIP-20 token.
interface IERC4626 {
    function decimals() external view returns (uint8 decimals);
    
    /// @notice The address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    function asset() external view returns (IERC20 assetTokenAddress);

    /// @notice Total amount of the underlying asset that is “managed” by Vault.
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @notice The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @notice The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @notice Maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @notice Mints shares Vault shares to receiver by depositing exactly assets of underlying tokens.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /// @notice Maximum amount of shares that can be minted from the Vault for the receiver, through a mint call.
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @notice Mints exactly shares Vault shares to receiver by depositing assets of underlying tokens.
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /// @notice Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /// @notice Maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    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);
}
          

src/tokens/ERC4626.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
import {ERC20, ERC20WithSupply} from "BoringSolidity/ERC20.sol";
import {BoringERC20} from "BoringSolidity/libraries/BoringERC20.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {IERC4626} from "interfaces/IERC4626.sol";

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

    error ErrZeroAssets();
    error ErrZeroShares();

    string public name;
    string public symbol;
    ERC20 public _asset;
    uint256 internal _totalAssets;

    function asset() external view returns (IERC20) {
        return _asset;
    }

    function decimals() external view returns (uint8) {
        return _asset.safeDecimals();
    }

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        if ((shares = previewDeposit(assets)) == 0) {
            revert ErrZeroShares();
        }

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

        unchecked {
            _totalAssets += 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);

        unchecked {
            _totalAssets += 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);

        unchecked {
            _totalAssets -= 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.
        if ((assets = previewRedeem(shares)) == 0) {
            revert ErrZeroAssets();
        }

        _beforeWithdraw(assets, shares);
        _burn(owner, shares);
        emit Withdraw(msg.sender, receiver, owner, assets, shares);
        _asset.safeTransfer(receiver, assets);

        unchecked {
            _totalAssets -= assets;
        }
    }

    function totalAssets() public view virtual returns (uint256) {
        return _totalAssets;
    }

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        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;
        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply;
        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

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

    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];
    }

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

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

src/tokens/MagicGlp.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "BoringSolidity/BoringOwnable.sol";
import "./ERC4626.sol";

contract MagicGlpData is ERC4626, BoringOwnable {
    error ErrNotStrategyExecutor(address);

    address public rewardHandler;
    mapping(address => bool) public strategyExecutors;

    modifier onlyStrategyExecutor() {
        if (msg.sender != owner && !strategyExecutors[msg.sender]) {
            revert ErrNotStrategyExecutor(msg.sender);
        }
        _;
    }
}

/// @dev Vault version of the GLP Wrapper, auto compound yield increase shares value
contract MagicGlp is MagicGlpData {
    event LogRewardHandlerChanged(address indexed previous, address indexed current);
    event LogStrategyExecutorChanged(address indexed executor, bool allowed);
    event LogStakedGlpChanged(ERC20 indexed previous, ERC20 indexed current);

    constructor(
        ERC20 __asset,
        string memory _name,
        string memory _symbol
    ) {
        _asset = __asset;
        name = _name;
        symbol = _symbol;
    }

    function setStrategyExecutor(address executor, bool value) external onlyOwner {
        strategyExecutors[executor] = value;
        emit LogStrategyExecutorChanged(executor, value);
    }

    function setRewardHandler(address _rewardHandler) external onlyOwner {
        emit LogRewardHandlerChanged(rewardHandler, _rewardHandler);
        rewardHandler = _rewardHandler;
    }

    function setStakedGlp(ERC20 _sGlp) external onlyOwner {
        emit LogStakedGlpChanged(_asset, _sGlp);
        _asset = _sGlp;
    }

    // Forward unknown function calls to the reward handler.
    fallback() external {
        _delegate(rewardHandler);
    }

    /**
     * From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol
     *
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) private {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}
          

Compiler Settings

{"remappings":["/=src/","BoringSolidity/=lib/BoringSolidity/contracts/","ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/","cauldrons/=src/cauldrons/","ds-test/=lib/forge-std/lib/ds-test/src/","forge-deploy/=lib/forge-deploy/contracts/","forge-std/=lib/forge-std/src/","generated/=generated/","interfaces/=src/interfaces/","lenses/=src/lenses/","libraries/=src/libraries/","mixins/=src/mixins/","openzeppelin-contracts-upgradeabl/=lib/openzeppelin-contracts-upgradeable/contracts/","openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/","oracles/=src/oracles/","periphery/=src/periphery/","solady/=lib/solady/src/","solmate/=lib/solmate/src/","strategies/=src/strategies/","surl/=lib/surl/src/","swappers/=src/swappers/","tokens/=src/tokens/","utils/=utils/"],"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":800,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"paris"}
              

Contract ABI

[{"type":"error","name":"ErrNotStrategyExecutor","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"error","name":"ErrZeroAssets","inputs":[]},{"type":"error","name":"ErrZeroShares","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogDistributeRewards","inputs":[{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogHarvest","inputs":[],"anonymous":false},{"type":"event","name":"LogRewardRouterChanged","inputs":[{"type":"address","name":"previous","internalType":"contract IKlpRewardHandler","indexed":true},{"type":"address","name":"current","internalType":"contract IKlpRewardHandler","indexed":true}],"anonymous":false},{"type":"event","name":"LogRewardTokensChanged","inputs":[{"type":"address[]","name":"previous","internalType":"contract IERC20[]","indexed":false},{"type":"address[]","name":"current","internalType":"contract IERC20[]","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Withdraw","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ERC20"}],"name":"_asset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"asset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"convertToAssets","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"convertToShares","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"shares","internalType":"uint256"}],"name":"deposit","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"distributeRewards","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"harvest","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxDeposit","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxMint","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxRedeem","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxWithdraw","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"assets","internalType":"uint256"}],"name":"mint","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewDeposit","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewMint","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewRedeem","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewWithdraw","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"assets","internalType":"uint256"}],"name":"redeem","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"rewardHandler","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IKlpRewardHandler"}],"name":"rewardRouter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"rewardTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardRouter","inputs":[{"type":"address","name":"_rewardRouter","internalType":"contract IKlpRewardHandler"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardTokens","inputs":[{"type":"address[]","name":"_rewardTokens","internalType":"contract IERC20[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"skimAssets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"strategyExecutors","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAssets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"},{"type":"bool","name":"direct","internalType":"bool"},{"type":"bool","name":"renounce","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"shares","internalType":"uint256"}],"name":"withdraw","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"owner","internalType":"address"}]}]
              

Contract Creation Code

0x60c060405234801561001057600080fd5b504660a081905261007490604080517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186020820152908101829052306060820152600090608001604051602081830303815290604052805190602001209050919050565b608052600880546001600160a01b031916339081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a360805160a0516124dc6100dc6000396000611a0101526000611a3201526124dc6000f3fe608060405234801561001057600080fd5b50600436106102de5760003560e01c806370a0823111610186578063ba087652116100e3578063d84872d111610097578063e30c397811610071578063e30c3978146105ff578063ef8b30f714610612578063fcea5e291461062557600080fd5b8063d84872d1146105a3578063d905777e146105ab578063dd62ed3e146105d457600080fd5b8063c6e6f592116100c8578063c6e6f5921461056a578063ce96cb771461057d578063d505accf1461059057600080fd5b8063ba08765214610557578063c63d75b6146103e357600080fd5b806395d89b411161013a578063a9059cbb1161011f578063a9059cbb1461051e578063b3d7f6b914610531578063b460af941461054457600080fd5b806395d89b4114610503578063977b91d71461050b57600080fd5b80637ecebe001161016b5780637ecebe00146104bd5780638da5cb5b146104dd57806394bf804d146104f057600080fd5b806370a082311461048a5780637bb7bed1146104aa57600080fd5b80633644e5151161023f5780634e71e0c8116101f35780635a3bb989116101cd5780635a3bb98914610451578063670fb821146104645780636e553f651461047757600080fd5b80634e71e0c8146104135780635066ebdd1461041b57806359974e381461043e57600080fd5b8063402d267d11610224578063402d267d146103e35780634641257d146103f85780634cdad5061461040057600080fd5b80633644e515146103b657806338d52e0f146103be57600080fd5b80630a28a47711610296578063201e81a81161027b578063201e81a81461037657806323b872dd14610389578063313ce5671461039c57600080fd5b80630a28a4771461035a57806318160ddd1461036d57600080fd5b8063078dfbe7116102c7578063078dfbe71461030f57806307a2d13a14610324578063095ea7b31461033757600080fd5b806301e1d114146102e357806306fdde03146102fa575b600080fd5b6007545b6040519081526020015b60405180910390f35b610302610638565b6040516102f19190611f8b565b61032261031d366004611fe4565b6106c6565b005b6102e761033236600461202f565b610813565b61034a610345366004612048565b610841565b60405190151581526020016102f1565b6102e761036836600461202f565b6108ae565b6102e760035481565b61032261038436600461209a565b6108cf565b61034a61039736600461215f565b61097a565b6103a4610ba6565b60405160ff90911681526020016102f1565b6102e7610bc3565b6006546001600160a01b03165b6040516001600160a01b0390911681526020016102f1565b6102e76103f13660046121a0565b5060001990565b610322610bcd565b6102e761040e36600461202f565b610d73565b610322610d7e565b61034a6104293660046121a0565b600b6020526000908152604090205460ff1681565b61032261044c36600461202f565b610e3c565b600c546103cb906001600160a01b031681565b600a546103cb906001600160a01b031681565b6102e76104853660046121bd565b610f50565b6102e76104983660046121a0565b60006020819052908152604090205481565b6103cb6104b836600461202f565b610ff4565b6102e76104cb3660046121a0565b60026020526000908152604090205481565b6008546103cb906001600160a01b031681565b6102e76104fe3660046121bd565b61101e565b610302611099565b6103226105193660046121a0565b6110a6565b61034a61052c366004612048565b61115b565b6102e761053f36600461202f565b6112c0565b6102e76105523660046121ed565b6112e0565b6102e76105653660046121ed565b6113d9565b6102e761057836600461202f565b6114f1565b6102e761058b3660046121a0565b611512565b61032261059e366004612233565b611534565b6102e7611796565b6102e76105b93660046121a0565b6001600160a01b031660009081526020819052604090205490565b6102e76105e23660046122a4565b600160209081526000928352604080842090915290825290205481565b6009546103cb906001600160a01b031681565b6102e761062036600461202f565b6118ee565b6006546103cb906001600160a01b031681565b60048054610645906122d2565b80601f0160208091040260200160405190810160405280929190818152602001828054610671906122d2565b80156106be5780601f10610693576101008083540402835291602001916106be565b820191906000526020600020905b8154815290600101906020018083116106a157829003601f168201915b505050505081565b6008546001600160a01b031633146107255760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b81156107f1576001600160a01b03831615158061073f5750805b61078b5760405162461bcd60e51b815260206004820152601560248201527f4f776e61626c653a207a65726f20616464726573730000000000000000000000604482015260640161071c565b6008546040516001600160a01b038086169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600880546001600160a01b0385166001600160a01b031991821617909155600980549091169055505050565b600980546001600160a01b0385166001600160a01b0319909116179055505050565b60035460009080156108385761083361082b60075490565b8490836118f9565b61083a565b825b9392505050565b3360008181526001602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061089c9086815260200190565b60405180910390a35060015b92915050565b600354600090801561083857610833816108c760075490565b859190611917565b6008546001600160a01b031633146109295760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b7f93d0db3b60b688fe855bef0b399c5563c5f5c7ead0e14d611f0e2509941c8c74600d8260405161095b92919061230c565b60405180910390a1805161097690600d906020840190611ef6565b5050565b60008115610b4f576001600160a01b038416600090815260208190526040902054828110156109eb5760405162461bcd60e51b815260206004820152601660248201527f45524332303a2062616c616e636520746f6f206c6f7700000000000000000000604482015260640161071c565b836001600160a01b0316856001600160a01b031614610b4d576001600160a01b03851660009081526001602090815260408083203384529091529020546000198114610ab05783811015610a815760405162461bcd60e51b815260206004820152601860248201527f45524332303a20616c6c6f77616e636520746f6f206c6f770000000000000000604482015260640161071c565b610a8b84826123bb565b6001600160a01b03871660009081526001602090815260408083203384529091529020555b6001600160a01b038516610b065760405162461bcd60e51b815260206004820152601660248201527f45524332303a206e6f207a65726f206164647265737300000000000000000000604482015260640161071c565b610b1084836123bb565b6001600160a01b038088166000908152602081905260408082209390935590871681529081208054869290610b469084906123ce565b9091555050505b505b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b9491815260200190565b60405180910390a35060019392505050565b600654600090610bbe906001600160a01b031661193d565b905090565b6000610bbe6119fd565b6008546001600160a01b03163314801590610bf85750336000908152600b602052604090205460ff16155b15610c1857604051630aa4ca3560e01b815233600482015260240161071c565b600c54604051630e7cf6c160e41b815260006004820181905260248201526001600160a01b039091169063e7cf6c1090604401600060405180830381600087803b158015610c6557600080fd5b505af1158015610c79573d6000803e3d6000fd5b5050505060005b600d54811015610d47576000600d8281548110610c9f57610c9f6123e1565b60009182526020822001546040516370a0823160e01b81523060048201526001600160a01b03909116925082906370a0823190602401602060405180830381865afa158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1691906123f7565b90508015610d3257610d326001600160a01b0383163383611a54565b50508080610d3f90612410565b915050610c80565b506040517f8c1869459e5f58cb5da1a5afadeca5908071de231a02c530c9fb446a0ce5efe990600090a1565b60006108a882610813565b6009546001600160a01b0316338114610dd95760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e6572604482015260640161071c565b6008546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600880546001600160a01b039092166001600160a01b0319928316179055600980549091169055565b6008546001600160a01b03163314801590610e675750336000908152600b602052604090205460ff16155b15610e8757604051630aa4ca3560e01b815233600482015260240161071c565b6006546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610ede573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f029190612429565b508060076000828254610f1591906123ce565b90915550506040518181527f9551c6bf12bd6b4262b9cd8587846e73d0c64d9c261a1cb4bf7570300aaa4ba79060200160405180910390a150565b6000610f5b836118ee565b905080600003610f7e5760405163857ad50560e01b815260040160405180910390fd5b600654610f96906001600160a01b0316333086611b6f565b6007805484019055610fa88282611c93565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a36108a8565b600d818154811061100457600080fd5b6000918252602090912001546001600160a01b0316905081565b6000611029836112c0565b600654909150611044906001600160a01b0316333084611b6f565b60078054820190556110568284611c93565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101610fe7565b60058054610645906122d2565b6008546001600160a01b031633146111005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b600c546040516001600160a01b038084169216907e790b5eda1e220d46d3a40810ec6bf0f7d0324961c550e1649146ede156980390600090a3600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6000811515806111735750336001600160a01b038416145b156112835733600090815260208190526040902054828110156111d85760405162461bcd60e51b815260206004820152601660248201527f45524332303a2062616c616e636520746f6f206c6f7700000000000000000000604482015260640161071c565b336001600160a01b03851614611281576001600160a01b03841661123e5760405162461bcd60e51b815260206004820152601660248201527f45524332303a206e6f207a65726f206164647265737300000000000000000000604482015260640161071c565b61124883826123bb565b33600090815260208190526040808220929092556001600160a01b0386168152908120805485929061127b9084906123ce565b90915550505b505b6040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161089c565b6003546000908015610838576108336112d860075490565b849083611917565b60006112eb846108ae565b9050336001600160a01b0383161461135b576001600160a01b038216600090815260016020908152604080832033845290915290205460001981146113595761133482826123bb565b6001600160a01b03841660009081526001602090815260408083203384529091529020555b505b6113658282611d6f565b60408051858152602081018390526001600160a01b03808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46006546113c7906001600160a01b03168486611a54565b60078054949094039093555090919050565b6000336001600160a01b03831614611449576001600160a01b038216600090815260016020908152604080832033845290915290205460001981146114475761142285826123bb565b6001600160a01b03841660009081526001602090815260408083203384529091529020555b505b61145284610d73565b90508060000361147557604051633fa9a1b360e01b815260040160405180910390fd5b61147f8285611d6f565b60408051828152602081018690526001600160a01b03808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46006546114e1906001600160a01b03168483611a54565b6007805482900390559392505050565b6003546000908015610838576108338161150a60075490565b8591906118f9565b6001600160a01b0381166000908152602081905260408120546108a890610813565b6001600160a01b03871661158a5760405162461bcd60e51b815260206004820152601860248201527f45524332303a204f776e65722063616e6e6f7420626520300000000000000000604482015260640161071c565b8342106115d95760405162461bcd60e51b815260206004820152600e60248201527f45524332303a2045787069726564000000000000000000000000000000000000604482015260640161071c565b6001600160a01b03871660008181526002602052604081208054600192611683927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928d928d928d929161162c83612410565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810188905260e00160405160208183030381529060405280519060200120611e60565b6040805160008152602081018083529290925260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156116d1573d6000803e3d6000fd5b505050602060405103516001600160a01b0316146117315760405162461bcd60e51b815260206004820152601860248201527f45524332303a20496e76616c6964205369676e61747572650000000000000000604482015260640161071c565b6001600160a01b038781166000818152600160209081526040808320948b168084529482529182902089905590518881527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6008546000906001600160a01b031633146117f35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b6007546006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561183e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186291906123f7565b61186c91906123bb565b905080156118eb5760065460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156118c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e99190612429565b505b90565b60006108a8826114f1565b600082600019048411830215820261191057600080fd5b5091020490565b600082600019048411830215820261192e57600080fd5b50910281810615159190040190565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b1790529051600091829182916001600160a01b038616916119839190612446565b600060405180830381855afa9150503d80600081146119be576040519150601f19603f3d011682016040523d82523d6000602084013e6119c3565b606091505b50915091508180156119d6575080516020145b6119e15760126119f5565b808060200190518101906119f59190612462565b949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614611a2f57610bbe46611eb5565b507f000000000000000000000000000000000000000000000000000000000000000090565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611ab09190612446565b6000604051808303816000865af19150503d8060008114611aed576040519150601f19603f3d011682016040523d82523d6000602084013e611af2565b606091505b5091509150818015611b1c575080511580611b1c575080806020019051810190611b1c9190612429565b611b685760405162461bcd60e51b815260206004820152601c60248201527f426f72696e6745524332303a205472616e73666572206661696c656400000000604482015260640161071c565b5050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691611bd39190612446565b6000604051808303816000865af19150503d8060008114611c10576040519150601f19603f3d011682016040523d82523d6000602084013e611c15565b606091505b5091509150818015611c3f575080511580611c3f575080806020019051810190611c3f9190612429565b611c8b5760405162461bcd60e51b815260206004820181905260248201527f426f72696e6745524332303a205472616e7366657246726f6d206661696c6564604482015260640161071c565b505050505050565b600081600354611ca391906123ce565b9050600354811015611cf75760405162461bcd60e51b815260206004820152600d60248201527f4d696e74206f766572666c6f7700000000000000000000000000000000000000604482015260640161071c565b60038190556001600160a01b03831660009081526020819052604081208054849290611d249084906123ce565b90915550506040518281526001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6001600160a01b038216600090815260208190526040902054811115611dd75760405162461bcd60e51b815260206004820152600d60248201527f4275726e20746f6f206d75636800000000000000000000000000000000000000604482015260640161071c565b8060036000828254611de991906123bb565b90915550506001600160a01b03821660009081526020819052604081208054839290611e169084906123bb565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060405180604001604052806002815260200161190160f01b815250611e856119fd565b83604051602001611e989392919061247f565b604051602081830303815290604052805190602001209050919050565b604080517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186020820152908101829052306060820152600090608001611e98565b828054828255906000526020600020908101928215611f4b579160200282015b82811115611f4b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611f16565b506118e99291505b808211156118e95760008155600101611f53565b60005b83811015611f82578181015183820152602001611f6a565b50506000910152565b6020815260008251806020840152611faa816040850160208701611f67565b601f01601f19169190910160400192915050565b6001600160a01b0381168114611fd357600080fd5b50565b8015158114611fd357600080fd5b600080600060608486031215611ff957600080fd5b833561200481611fbe565b9250602084013561201481611fd6565b9150604084013561202481611fd6565b809150509250925092565b60006020828403121561204157600080fd5b5035919050565b6000806040838503121561205b57600080fd5b823561206681611fbe565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b803561209581611fbe565b919050565b600060208083850312156120ad57600080fd5b823567ffffffffffffffff808211156120c557600080fd5b818501915085601f8301126120d957600080fd5b8135818111156120eb576120eb612074565b8060051b604051601f19603f8301168101818110858211171561211057612110612074565b60405291825284820192508381018501918883111561212e57600080fd5b938501935b82851015612153576121448561208a565b84529385019392850192612133565b98975050505050505050565b60008060006060848603121561217457600080fd5b833561217f81611fbe565b9250602084013561218f81611fbe565b929592945050506040919091013590565b6000602082840312156121b257600080fd5b813561083a81611fbe565b600080604083850312156121d057600080fd5b8235915060208301356121e281611fbe565b809150509250929050565b60008060006060848603121561220257600080fd5b83359250602084013561221481611fbe565b9150604084013561202481611fbe565b60ff81168114611fd357600080fd5b600080600080600080600060e0888a03121561224e57600080fd5b873561225981611fbe565b9650602088013561226981611fbe565b95506040880135945060608801359350608088013561228781612224565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156122b757600080fd5b82356122c281611fbe565b915060208301356121e281611fbe565b600181811c908216806122e657607f821691505b60208210810361230657634e487b7160e01b600052602260045260246000fd5b50919050565b6000604082016040835280855480835260608501915086600052602092508260002060005b828110156123585781546001600160a01b0316845260208401935060019182019101612331565b5050508381038285015284518082528583019183019060005b818110156123985783516001600160a01b0316835260208301938501939250600101612371565b5090979650505050505050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108a8576108a86123a5565b808201808211156108a8576108a86123a5565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561240957600080fd5b5051919050565b600060018201612422576124226123a5565b5060010190565b60006020828403121561243b57600080fd5b815161083a81611fd6565b60008251612458818460208701611f67565b9190910192915050565b60006020828403121561247457600080fd5b815161083a81612224565b60008451612491818460208901611f67565b9190910192835250602082015260400191905056fea26469706673582212207b7ee8c9578af7955b0c59157074eb997fce0c9a542e808b5204b5de70fb890a64736f6c63430008140033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106102de5760003560e01c806370a0823111610186578063ba087652116100e3578063d84872d111610097578063e30c397811610071578063e30c3978146105ff578063ef8b30f714610612578063fcea5e291461062557600080fd5b8063d84872d1146105a3578063d905777e146105ab578063dd62ed3e146105d457600080fd5b8063c6e6f592116100c8578063c6e6f5921461056a578063ce96cb771461057d578063d505accf1461059057600080fd5b8063ba08765214610557578063c63d75b6146103e357600080fd5b806395d89b411161013a578063a9059cbb1161011f578063a9059cbb1461051e578063b3d7f6b914610531578063b460af941461054457600080fd5b806395d89b4114610503578063977b91d71461050b57600080fd5b80637ecebe001161016b5780637ecebe00146104bd5780638da5cb5b146104dd57806394bf804d146104f057600080fd5b806370a082311461048a5780637bb7bed1146104aa57600080fd5b80633644e5151161023f5780634e71e0c8116101f35780635a3bb989116101cd5780635a3bb98914610451578063670fb821146104645780636e553f651461047757600080fd5b80634e71e0c8146104135780635066ebdd1461041b57806359974e381461043e57600080fd5b8063402d267d11610224578063402d267d146103e35780634641257d146103f85780634cdad5061461040057600080fd5b80633644e515146103b657806338d52e0f146103be57600080fd5b80630a28a47711610296578063201e81a81161027b578063201e81a81461037657806323b872dd14610389578063313ce5671461039c57600080fd5b80630a28a4771461035a57806318160ddd1461036d57600080fd5b8063078dfbe7116102c7578063078dfbe71461030f57806307a2d13a14610324578063095ea7b31461033757600080fd5b806301e1d114146102e357806306fdde03146102fa575b600080fd5b6007545b6040519081526020015b60405180910390f35b610302610638565b6040516102f19190611f8b565b61032261031d366004611fe4565b6106c6565b005b6102e761033236600461202f565b610813565b61034a610345366004612048565b610841565b60405190151581526020016102f1565b6102e761036836600461202f565b6108ae565b6102e760035481565b61032261038436600461209a565b6108cf565b61034a61039736600461215f565b61097a565b6103a4610ba6565b60405160ff90911681526020016102f1565b6102e7610bc3565b6006546001600160a01b03165b6040516001600160a01b0390911681526020016102f1565b6102e76103f13660046121a0565b5060001990565b610322610bcd565b6102e761040e36600461202f565b610d73565b610322610d7e565b61034a6104293660046121a0565b600b6020526000908152604090205460ff1681565b61032261044c36600461202f565b610e3c565b600c546103cb906001600160a01b031681565b600a546103cb906001600160a01b031681565b6102e76104853660046121bd565b610f50565b6102e76104983660046121a0565b60006020819052908152604090205481565b6103cb6104b836600461202f565b610ff4565b6102e76104cb3660046121a0565b60026020526000908152604090205481565b6008546103cb906001600160a01b031681565b6102e76104fe3660046121bd565b61101e565b610302611099565b6103226105193660046121a0565b6110a6565b61034a61052c366004612048565b61115b565b6102e761053f36600461202f565b6112c0565b6102e76105523660046121ed565b6112e0565b6102e76105653660046121ed565b6113d9565b6102e761057836600461202f565b6114f1565b6102e761058b3660046121a0565b611512565b61032261059e366004612233565b611534565b6102e7611796565b6102e76105b93660046121a0565b6001600160a01b031660009081526020819052604090205490565b6102e76105e23660046122a4565b600160209081526000928352604080842090915290825290205481565b6009546103cb906001600160a01b031681565b6102e761062036600461202f565b6118ee565b6006546103cb906001600160a01b031681565b60048054610645906122d2565b80601f0160208091040260200160405190810160405280929190818152602001828054610671906122d2565b80156106be5780601f10610693576101008083540402835291602001916106be565b820191906000526020600020905b8154815290600101906020018083116106a157829003601f168201915b505050505081565b6008546001600160a01b031633146107255760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b81156107f1576001600160a01b03831615158061073f5750805b61078b5760405162461bcd60e51b815260206004820152601560248201527f4f776e61626c653a207a65726f20616464726573730000000000000000000000604482015260640161071c565b6008546040516001600160a01b038086169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600880546001600160a01b0385166001600160a01b031991821617909155600980549091169055505050565b600980546001600160a01b0385166001600160a01b0319909116179055505050565b60035460009080156108385761083361082b60075490565b8490836118f9565b61083a565b825b9392505050565b3360008181526001602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061089c9086815260200190565b60405180910390a35060015b92915050565b600354600090801561083857610833816108c760075490565b859190611917565b6008546001600160a01b031633146109295760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b7f93d0db3b60b688fe855bef0b399c5563c5f5c7ead0e14d611f0e2509941c8c74600d8260405161095b92919061230c565b60405180910390a1805161097690600d906020840190611ef6565b5050565b60008115610b4f576001600160a01b038416600090815260208190526040902054828110156109eb5760405162461bcd60e51b815260206004820152601660248201527f45524332303a2062616c616e636520746f6f206c6f7700000000000000000000604482015260640161071c565b836001600160a01b0316856001600160a01b031614610b4d576001600160a01b03851660009081526001602090815260408083203384529091529020546000198114610ab05783811015610a815760405162461bcd60e51b815260206004820152601860248201527f45524332303a20616c6c6f77616e636520746f6f206c6f770000000000000000604482015260640161071c565b610a8b84826123bb565b6001600160a01b03871660009081526001602090815260408083203384529091529020555b6001600160a01b038516610b065760405162461bcd60e51b815260206004820152601660248201527f45524332303a206e6f207a65726f206164647265737300000000000000000000604482015260640161071c565b610b1084836123bb565b6001600160a01b038088166000908152602081905260408082209390935590871681529081208054869290610b469084906123ce565b9091555050505b505b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b9491815260200190565b60405180910390a35060019392505050565b600654600090610bbe906001600160a01b031661193d565b905090565b6000610bbe6119fd565b6008546001600160a01b03163314801590610bf85750336000908152600b602052604090205460ff16155b15610c1857604051630aa4ca3560e01b815233600482015260240161071c565b600c54604051630e7cf6c160e41b815260006004820181905260248201526001600160a01b039091169063e7cf6c1090604401600060405180830381600087803b158015610c6557600080fd5b505af1158015610c79573d6000803e3d6000fd5b5050505060005b600d54811015610d47576000600d8281548110610c9f57610c9f6123e1565b60009182526020822001546040516370a0823160e01b81523060048201526001600160a01b03909116925082906370a0823190602401602060405180830381865afa158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1691906123f7565b90508015610d3257610d326001600160a01b0383163383611a54565b50508080610d3f90612410565b915050610c80565b506040517f8c1869459e5f58cb5da1a5afadeca5908071de231a02c530c9fb446a0ce5efe990600090a1565b60006108a882610813565b6009546001600160a01b0316338114610dd95760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e6572604482015260640161071c565b6008546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600880546001600160a01b039092166001600160a01b0319928316179055600980549091169055565b6008546001600160a01b03163314801590610e675750336000908152600b602052604090205460ff16155b15610e8757604051630aa4ca3560e01b815233600482015260240161071c565b6006546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610ede573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f029190612429565b508060076000828254610f1591906123ce565b90915550506040518181527f9551c6bf12bd6b4262b9cd8587846e73d0c64d9c261a1cb4bf7570300aaa4ba79060200160405180910390a150565b6000610f5b836118ee565b905080600003610f7e5760405163857ad50560e01b815260040160405180910390fd5b600654610f96906001600160a01b0316333086611b6f565b6007805484019055610fa88282611c93565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a36108a8565b600d818154811061100457600080fd5b6000918252602090912001546001600160a01b0316905081565b6000611029836112c0565b600654909150611044906001600160a01b0316333084611b6f565b60078054820190556110568284611c93565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101610fe7565b60058054610645906122d2565b6008546001600160a01b031633146111005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b600c546040516001600160a01b038084169216907e790b5eda1e220d46d3a40810ec6bf0f7d0324961c550e1649146ede156980390600090a3600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6000811515806111735750336001600160a01b038416145b156112835733600090815260208190526040902054828110156111d85760405162461bcd60e51b815260206004820152601660248201527f45524332303a2062616c616e636520746f6f206c6f7700000000000000000000604482015260640161071c565b336001600160a01b03851614611281576001600160a01b03841661123e5760405162461bcd60e51b815260206004820152601660248201527f45524332303a206e6f207a65726f206164647265737300000000000000000000604482015260640161071c565b61124883826123bb565b33600090815260208190526040808220929092556001600160a01b0386168152908120805485929061127b9084906123ce565b90915550505b505b6040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161089c565b6003546000908015610838576108336112d860075490565b849083611917565b60006112eb846108ae565b9050336001600160a01b0383161461135b576001600160a01b038216600090815260016020908152604080832033845290915290205460001981146113595761133482826123bb565b6001600160a01b03841660009081526001602090815260408083203384529091529020555b505b6113658282611d6f565b60408051858152602081018390526001600160a01b03808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46006546113c7906001600160a01b03168486611a54565b60078054949094039093555090919050565b6000336001600160a01b03831614611449576001600160a01b038216600090815260016020908152604080832033845290915290205460001981146114475761142285826123bb565b6001600160a01b03841660009081526001602090815260408083203384529091529020555b505b61145284610d73565b90508060000361147557604051633fa9a1b360e01b815260040160405180910390fd5b61147f8285611d6f565b60408051828152602081018690526001600160a01b03808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46006546114e1906001600160a01b03168483611a54565b6007805482900390559392505050565b6003546000908015610838576108338161150a60075490565b8591906118f9565b6001600160a01b0381166000908152602081905260408120546108a890610813565b6001600160a01b03871661158a5760405162461bcd60e51b815260206004820152601860248201527f45524332303a204f776e65722063616e6e6f7420626520300000000000000000604482015260640161071c565b8342106115d95760405162461bcd60e51b815260206004820152600e60248201527f45524332303a2045787069726564000000000000000000000000000000000000604482015260640161071c565b6001600160a01b03871660008181526002602052604081208054600192611683927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928d928d928d929161162c83612410565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810188905260e00160405160208183030381529060405280519060200120611e60565b6040805160008152602081018083529290925260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156116d1573d6000803e3d6000fd5b505050602060405103516001600160a01b0316146117315760405162461bcd60e51b815260206004820152601860248201527f45524332303a20496e76616c6964205369676e61747572650000000000000000604482015260640161071c565b6001600160a01b038781166000818152600160209081526040808320948b168084529482529182902089905590518881527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6008546000906001600160a01b031633146117f35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161071c565b6007546006546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561183e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186291906123f7565b61186c91906123bb565b905080156118eb5760065460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156118c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e99190612429565b505b90565b60006108a8826114f1565b600082600019048411830215820261191057600080fd5b5091020490565b600082600019048411830215820261192e57600080fd5b50910281810615159190040190565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b1790529051600091829182916001600160a01b038616916119839190612446565b600060405180830381855afa9150503d80600081146119be576040519150601f19603f3d011682016040523d82523d6000602084013e6119c3565b606091505b50915091508180156119d6575080516020145b6119e15760126119f5565b808060200190518101906119f59190612462565b949350505050565b60007f00000000000000000000000000000000000000000000000000000000000008ae4614611a2f57610bbe46611eb5565b507fcc12223b51065c5d52dd4b40664b13bf75bc4465c45be13598d8a1125e4828f590565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611ab09190612446565b6000604051808303816000865af19150503d8060008114611aed576040519150601f19603f3d011682016040523d82523d6000602084013e611af2565b606091505b5091509150818015611b1c575080511580611b1c575080806020019051810190611b1c9190612429565b611b685760405162461bcd60e51b815260206004820152601c60248201527f426f72696e6745524332303a205472616e73666572206661696c656400000000604482015260640161071c565b5050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691611bd39190612446565b6000604051808303816000865af19150503d8060008114611c10576040519150601f19603f3d011682016040523d82523d6000602084013e611c15565b606091505b5091509150818015611c3f575080511580611c3f575080806020019051810190611c3f9190612429565b611c8b5760405162461bcd60e51b815260206004820181905260248201527f426f72696e6745524332303a205472616e7366657246726f6d206661696c6564604482015260640161071c565b505050505050565b600081600354611ca391906123ce565b9050600354811015611cf75760405162461bcd60e51b815260206004820152600d60248201527f4d696e74206f766572666c6f7700000000000000000000000000000000000000604482015260640161071c565b60038190556001600160a01b03831660009081526020819052604081208054849290611d249084906123ce565b90915550506040518281526001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6001600160a01b038216600090815260208190526040902054811115611dd75760405162461bcd60e51b815260206004820152600d60248201527f4275726e20746f6f206d75636800000000000000000000000000000000000000604482015260640161071c565b8060036000828254611de991906123bb565b90915550506001600160a01b03821660009081526020819052604081208054839290611e169084906123bb565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060405180604001604052806002815260200161190160f01b815250611e856119fd565b83604051602001611e989392919061247f565b604051602081830303815290604052805190602001209050919050565b604080517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186020820152908101829052306060820152600090608001611e98565b828054828255906000526020600020908101928215611f4b579160200282015b82811115611f4b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611f16565b506118e99291505b808211156118e95760008155600101611f53565b60005b83811015611f82578181015183820152602001611f6a565b50506000910152565b6020815260008251806020840152611faa816040850160208701611f67565b601f01601f19169190910160400192915050565b6001600160a01b0381168114611fd357600080fd5b50565b8015158114611fd357600080fd5b600080600060608486031215611ff957600080fd5b833561200481611fbe565b9250602084013561201481611fd6565b9150604084013561202481611fd6565b809150509250925092565b60006020828403121561204157600080fd5b5035919050565b6000806040838503121561205b57600080fd5b823561206681611fbe565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b803561209581611fbe565b919050565b600060208083850312156120ad57600080fd5b823567ffffffffffffffff808211156120c557600080fd5b818501915085601f8301126120d957600080fd5b8135818111156120eb576120eb612074565b8060051b604051601f19603f8301168101818110858211171561211057612110612074565b60405291825284820192508381018501918883111561212e57600080fd5b938501935b82851015612153576121448561208a565b84529385019392850192612133565b98975050505050505050565b60008060006060848603121561217457600080fd5b833561217f81611fbe565b9250602084013561218f81611fbe565b929592945050506040919091013590565b6000602082840312156121b257600080fd5b813561083a81611fbe565b600080604083850312156121d057600080fd5b8235915060208301356121e281611fbe565b809150509250929050565b60008060006060848603121561220257600080fd5b83359250602084013561221481611fbe565b9150604084013561202481611fbe565b60ff81168114611fd357600080fd5b600080600080600080600060e0888a03121561224e57600080fd5b873561225981611fbe565b9650602088013561226981611fbe565b95506040880135945060608801359350608088013561228781612224565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156122b757600080fd5b82356122c281611fbe565b915060208301356121e281611fbe565b600181811c908216806122e657607f821691505b60208210810361230657634e487b7160e01b600052602260045260246000fd5b50919050565b6000604082016040835280855480835260608501915086600052602092508260002060005b828110156123585781546001600160a01b0316845260208401935060019182019101612331565b5050508381038285015284518082528583019183019060005b818110156123985783516001600160a01b0316835260208301938501939250600101612371565b5090979650505050505050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108a8576108a86123a5565b808201808211156108a8576108a86123a5565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561240957600080fd5b5051919050565b600060018201612422576124226123a5565b5060010190565b60006020828403121561243b57600080fd5b815161083a81611fd6565b60008251612458818460208701611f67565b9190910192915050565b60006020828403121561247457600080fd5b815161083a81612224565b60008451612491818460208901611f67565b9190910192835250602082015260400191905056fea26469706673582212207b7ee8c9578af7955b0c59157074eb997fce0c9a542e808b5204b5de70fb890a64736f6c63430008140033