S Price: $0.485597 (-2.44%)

Contract

0x880bE90E07fae33652111156f7F1540f9E40bBFb

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

2 Internal Transactions found.

Latest 2 internal transactions

Parent Transaction Hash Block From To
159910772025-03-26 1:33:5911 days ago1742952839
0x880bE90E...f9E40bBFb
 Contract Creation0 S
159910772025-03-26 1:33:5911 days ago1742952839  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BoringVault

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 17 : BoringVault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {BoringChef} from "src/boring-chef/BoringChef.sol";

contract BoringVault is BoringChef, ERC721Holder, ERC1155Holder {
    using Address for address;
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    // ========================================= STATE =========================================

    /**
     * @notice Contract responsbile for implementing `beforeTransfer`.
     */
    BeforeTransferHook public hook;

    //============================== EVENTS ===============================

    event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
    event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);

    //============================== CONSTRUCTOR ===============================

    constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
        BoringChef(_owner, _name, _symbol, _decimals)
    {}

    //============================== MANAGE ===============================

    /**
     * @notice Allows manager to make an arbitrary function call from this contract.
     * @dev Callable by MANAGER_ROLE.
     */
    function manage(address target, bytes calldata data, uint256 value)
        external
        requiresAuth
        returns (bytes memory result)
    {
        // Start a new epoch on every rebalance
        _rollOverEpoch();

        // Continue with rebalance
        result = target.functionCallWithValue(data, value);
    }

    /**
     * @notice Allows manager to make arbitrary function calls from this contract.
     * @dev Callable by MANAGER_ROLE.
     */
    function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
        external
        requiresAuth
        returns (bytes[] memory results)
    {
        // Start a new epoch on every rebalance
        _rollOverEpoch();

        // Continue with rebalance
        uint256 targetsLength = targets.length;
        results = new bytes[](targetsLength);
        for (uint256 i; i < targetsLength; ++i) {
            results[i] = targets[i].functionCallWithValue(data[i], values[i]);
        }
    }

    //============================== ENTER ===============================

    /**
     * @notice Allows minter to mint shares, in exchange for assets.
     * @dev If assetAmount is zero, no assets are transferred in.
     * @dev Callable by MINTER_ROLE.
     */
    function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
        external
        requiresAuth
    {
        // Transfer assets in
        if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);

        // Mint shares.
        _mint(to, shareAmount);

        emit Enter(from, address(asset), assetAmount, to, shareAmount);
    }

    //============================== EXIT ===============================

    /**
     * @notice Allows burner to burn shares, in exchange for assets.
     * @dev If assetAmount is zero, no assets are transferred out.
     * @dev Callable by BURNER_ROLE.
     */
    function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
        external
        requiresAuth
    {
        // Burn shares.
        _burn(from, shareAmount);

        // Transfer assets out.
        if (assetAmount > 0) asset.safeTransfer(to, assetAmount);

        emit Exit(to, address(asset), assetAmount, from, shareAmount);
    }

    //============================== BEFORE TRANSFER HOOK ===============================
    /**
     * @notice Sets the share locker.
     * @notice If set to zero address, the share locker logic is disabled.
     * @dev Callable by OWNER_ROLE.
     */
    function setBeforeTransferHook(address _hook) external requiresAuth {
        hook = BeforeTransferHook(_hook);
    }

    /**
     * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
     */
    function _callBeforeTransfer(address from, address to) internal view {
        if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
    }

    function transfer(address to, uint256 amount) public override(BoringChef) returns (bool) {
        _callBeforeTransfer(msg.sender, to);
        return super.transfer(to, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public override(BoringChef) returns (bool) {
        _callBeforeTransfer(from, to);
        return super.transferFrom(from, to, amount);
    }

    //============================== RECEIVE ===============================

    receive() external payable {}
}

File 2 of 17 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 3 of 17 : ERC721Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
 * {IERC721-setApprovalForAll}.
 */
abstract contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

File 4 of 17 : ERC1155Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.20;

import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC1155Receiver} from "../IERC1155Receiver.sol";

/**
 * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC-1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 */
abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
    }

    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}

File 5 of 17 : 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))
        }
    }
}

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

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

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

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

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

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "APPROVE_FAILED");
    }
}

File 7 of 17 : BeforeTransferHook.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

interface BeforeTransferHook {
    function beforeTransfer(address from, address to, address operator) external view;
}

File 8 of 17 : Auth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnershipTransferred(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnershipTransferred(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

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

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) external view returns (bool);
}

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

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

File 10 of 17 : BoringChef.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {BoringSafe} from "./BoringSafe.sol";

/// @title BoringChef
/// @author Shivaansh Kapoor, Jet Jadeja, Jack Corddry
/// @notice A contract for reward accounting, retroactive distribution, and claims for share based vaults.
contract BoringChef is Auth, ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    error ArrayLengthMismatch();
    error NoFutureEpochRewards();
    error InvalidRewardCampaignDuration();
    error MustClaimAtLeastOneReward();
    error CannotClaimFutureReward();
    error RewardClaimedAlready(uint256 rewardId);
    error CannotDisableRewardAccrualMoreThanOnce();
    error CannotEnableRewardAccrualMoreThanOnce();
    error OnlyClaimant();

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

    event EpochStarted(uint256 indexed epoch, uint256 eligibleShares, uint256 startTimestamp);
    event UserRewardsClaimed(address indexed user, address indexed token, uint256 rewardId, uint256 amount);
    event RewardsDistributed(
        address indexed token, uint256 indexed startEpoch, uint256 indexed endEpoch, uint256 amount, uint256 rewardId
    );
    event UserDepositedIntoEpoch(address indexed user, uint256 indexed epoch, uint256 shareAmount);
    event UserWithdrawnFromEpoch(address indexed user, uint256 indexed epoch, uint256 shareAmount);

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/

    /// @dev A record of a user's balance changing at a specific epoch
    struct BalanceUpdate {
        /// @dev The epoch in which the deposit was made
        uint48 epoch;
        /// @dev The total number of shares the user has at this epoch
        uint128 totalSharesBalance;
    }

    /// @dev A record of an epoch
    struct Epoch {
        /// @dev The total number of shares eligible for rewards at this epoch
        /// This is not the total number of shares deposited, but the total number
        /// of shares that have been deposited and are eligible for rewards
        uint128 eligibleShares;
        /// @dev The timestamp at which the epoch starts
        uint64 startTimestamp;
        /// @dev The timestamp at which the epoch ends
        /// This is set to 0 if the epoch is not over
        uint64 endTimestamp;
    }

    /// @dev A record of a reward
    struct Reward {
        /// @dev The epoch at which the reward starts
        uint48 startEpoch;
        /// @dev The epoch at which the reward ends
        uint48 endEpoch;
        /// @dev The token being rewarded
        address token;
        /// @dev The rate at which the reward token is distributed per second
        uint256 rewardRate;
    }

    /*//////////////////////////////////////////////////////////////
                            STATE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// @dev A contract to hold rewards to make sure the BoringVault doesn't spend them
    BoringSafe public immutable boringSafe;

    /// @dev The current epoch
    uint48 public currentEpoch;

    /// @dev A record of all epochs
    mapping(uint48 => Epoch) public epochs;

    /// @dev Maps users to an array of their balance changes
    mapping(address user => BalanceUpdate[]) public balanceUpdates;

    /// @dev Maps rewards to reward IDs
    mapping(uint256 rewardId => Reward) public rewards;
    uint256 public maxRewardId;

    /// @dev Maps users to a boolean indicating if they have disabled reward accrual
    mapping(address user => bool isDisabled) public addressToIsDisabled;

    /// @dev Maps users to a claimant who can claim rewards on their behalf
    mapping(address user => address claimant) public addressToClaimant;

    /// @dev Nested mapping to efficiently keep track of claimed rewards per user
    /// @dev A rewardBucket contains batches of 256 contiguous rewardIds (Bucket 0: rewardIds 0-255, Bucket 1: rewardIds 256-527, ...)
    /// @dev claimedRewards is a 256 bit bit-field where each bit represents if a rewardId in that bucket (monotonically increasing) has been claimed.
    mapping(address user => mapping(uint256 rewardBucket => uint256 claimedRewards)) public
        userToRewardBucketToClaimedRewards;

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

    /// @notice Initialize the contract.
    /// @dev We do this by setting the share token and initializing the first epoch.
    /// @param _owner The owner of the BoringVault.
    /// @param _name The name of the share token.
    /// @param _symbol The symbol of the share token.
    /// @param _decimals The decimals of the share token.
    constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
        Auth(_owner, Authority(address(0)))
        ERC20(_name, _symbol, _decimals)
    {
        // Deploy the BoringSafe that the BoringChef will use for escrowing distributed rewards.
        boringSafe = new BoringSafe();
    }

    /*//////////////////////////////////////////////////////////////
                       REWARD DISTRIBUTION LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Roll over to the next epoch.
    /// @dev Can only be called by an authorized address.
    function rollOverEpoch() external requiresAuth {
        _rollOverEpoch();
    }

    /// @notice Disable reward accrual for a given address
    /// @dev Can only be called by an authorized address
    function disableRewardAccrual(address user) external requiresAuth {
        // Check that reward accrual hasn't been disabled already
        if (addressToIsDisabled[user] == true) {
            revert CannotDisableRewardAccrualMoreThanOnce();
        }
        // Decrease the user's participation by their entire balance
        // They won't be eligible for rewards from the current epoch onwards unless they are reenabled
        _decreaseCurrentAndNextEpochParticipation(user, uint128(balanceOf[user]));
        addressToIsDisabled[user] = true;
    }

    /// @notice Enable reward accrual for a given address
    /// @dev Can only be called by an authorized address
    function enableRewardAccrual(address user) external requiresAuth {
        // Check that reward accrual hasn't been enabled already
        if (addressToIsDisabled[user] == false) {
            revert CannotEnableRewardAccrualMoreThanOnce();
        }
        // Increase the user's participation by their entire balance
        // Their entire balance will be eligible for rewards from the next epoch onwards
        addressToIsDisabled[user] = false;
        _increaseNextEpochParticipation(user, uint128(balanceOf[user]));
    }

    /// @notice Assign a claimant to be able to claim rewards for a user.
    /// @dev Can only be called by an authorized address
    function assignClaimantForUser(address user, address claimant) external requiresAuth {
        // Set the claimant as the address that can claim rewards on the user's behalf
        addressToClaimant[user] = claimant;
    }

    /// @notice Distribute rewards retroactively to users deposited during a given epoch range for multiple campaigns.
    /// @dev Creates new Reward objects and stores them in the rewards mapping, and transfers the reward tokens to the BoringSafe.
    /// @param tokens Array of addresses for the reward tokens.
    /// @param amounts Array of reward token amounts to distribute.
    /// @param startEpochs Array of start epochs for each reward distribution.
    /// @param endEpochs Array of end epochs for each reward distribution.
    function distributeRewards(
        address[] calldata tokens,
        uint256[] calldata amounts,
        uint48[] calldata startEpochs,
        uint48[] calldata endEpochs
    ) external requiresAuth {
        // Cache length for gas op
        uint256 numRewards = tokens.length;
        // Ensure that all arrays are the same length.
        if (numRewards != amounts.length || numRewards != startEpochs.length || numRewards != endEpochs.length) {
            revert ArrayLengthMismatch();
        }

        // Loop over each set of parameters.
        for (uint256 i = 0; i < numRewards; ++i) {
            // Check that the start and end epochs are valid.
            if (startEpochs[i] > endEpochs[i]) {
                revert InvalidRewardCampaignDuration();
            }
            if (endEpochs[i] >= currentEpoch) {
                revert NoFutureEpochRewards();
            }

            // Get the start and end epoch data.
            Epoch storage startEpochData = epochs[startEpochs[i]];
            Epoch storage endEpochData = epochs[endEpochs[i]];

            // Create a new reward and update the max reward ID.
            rewards[maxRewardId] = Reward({
                startEpoch: startEpochs[i],
                endEpoch: endEpochs[i],
                token: tokens[i],
                // Calculate the reward rate over the epoch period.
                // Consider the case where endEpochData.endTimestamp == startEpochData.startTimestamp
                rewardRate: amounts[i].divWadDown(endEpochData.endTimestamp - startEpochData.startTimestamp)
            });

            // Transfer the reward tokens to the BoringSafe.
            ERC20(tokens[i]).safeTransferFrom(msg.sender, address(boringSafe), amounts[i]);

            // Emit an event for this reward distribution.
            emit RewardsDistributed(tokens[i], startEpochs[i], endEpochs[i], amounts[i], maxRewardId++);
        }
    }

    /*//////////////////////////////////////////////////////////////
                       REWARD CLAIMING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Claims rewards (specified as rewardIds) for the caller.
    /// @dev Should be permissionless in normal circumstances.
    /// @param rewardIds The rewardIds to claim rewards for.
    function claimRewards(uint256[] calldata rewardIds) external requiresAuth {
        // Claim rewards accrued to the caller
        _claimRewards(rewardIds, msg.sender);
    }

    /// @notice Claims rewards (specified as rewardIds) to the caller on behalf of the specified user.
    /// @dev Only the claimant for the specified user can call this function
    /// @param rewardIds The rewardIds to claim rewards for.
    /// @param user The address of the user to claim rewards on behalf of.
    function claimRewardsOnBehalfOfUser(uint256[] calldata rewardIds, address user) external requiresAuth {
        // Check the caller is the designated claimant for the user
        if (addressToClaimant[user] != msg.sender) revert OnlyClaimant();
        // Claim rewards accrued to the user to the claimant
        _claimRewards(rewardIds, user);
    }

    /*//////////////////////////////////////////////////////////////
                                TRANSFER LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Transfer shares
    /// @dev This function is overridden from the ERC20 implementation to account for incentives.
    function transfer(address to, uint256 amount) public virtual override(ERC20) returns (bool success) {
        // Transfer shares from msg.sender to "to"
        success = super.transfer(to, amount);

        // Account for the transfer for the sender and forfeit incentives for current epoch for msg.sender
        _decreaseCurrentAndNextEpochParticipation(msg.sender, uint128(amount));

        // Account for the transfer for the recipient and transfer incentives for next epoch onwards to "to"
        _increaseNextEpochParticipation(to, uint128(amount));
    }

    /// @notice Transfer shares from one user to another
    /// @dev This function is overridden from the ERC20 implementation to account for incentives.
    function transferFrom(address from, address to, uint256 amount)
        public
        virtual
        override(ERC20)
        returns (bool success)
    {
        // Transfer shares from "from" to "to"
        success = super.transferFrom(from, to, amount);

        // Account for the transfer for the sender and forfeit incentives for current epoch for "from"
        _decreaseCurrentAndNextEpochParticipation(from, uint128(amount));

        // Account for the transfer for the recipient and transfer incentives for next epoch onwards to "to"
        _increaseNextEpochParticipation(to, uint128(amount));
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Get all balance updates for a user.
    function getBalanceUpdates(address user) external view returns (BalanceUpdate[] memory) {
        return balanceUpdates[user];
    }

    /// @notice Get a user's reward balance for a given reward ID.
    /// @param user The user to get the reward balance for.
    /// @param rewardId The ID of the reward to get the balance for.
    function getUserRewardBalance(address user, uint256 rewardId) external view returns (uint256) {
        // Fetch all balance change updates for the caller.
        BalanceUpdate[] storage userBalanceUpdates = balanceUpdates[user];

        // Retrieve the reward ID, start epoch, and end epoch.
        Reward storage reward = rewards[rewardId];

        // Initialize a local accumulator for the total reward owed.
        uint256 rewardsOwed = 0;

        // Scale rate up by 1e18 to avoid precision loss
        uint256 rateWAD = reward.rewardRate * 1e18;

        // We want to iterate over the epoch range [startEpoch..endEpoch],
        // summing up the user's share of tokens from each epoch.
        for (uint48 epoch = reward.startEpoch; epoch <= reward.endEpoch; epoch++) {
            // Determine the user's share balance during this epoch.
            (, uint128 userBalanceAtEpoch) = _findLatestBalanceUpdateForEpoch(epoch, userBalanceUpdates);

            // If the user is owed rewards for this epoch, remit them
            if (userBalanceAtEpoch > 0) {
                Epoch storage epochData = epochs[epoch];
                // Compute user fraction = userBalance / totalShares.
                uint256 userFraction = uint256(userBalanceAtEpoch).divWadDown(epochData.eligibleShares);

                // Figure out how many tokens were distributed in this epoch
                // for the specified reward ID:
                uint256 epochDuration = epochData.endTimestamp - epochData.startTimestamp;
                uint256 epochReward = rateWAD.mulWadDown(epochDuration);

                // Multiply epochReward * fraction = userRewardThisEpoch.
                // Add that to rewardsOwed.
                rewardsOwed += epochReward.mulWadDown(userFraction);
            }
        }

        // Scale down by 1e18 to get the final reward amount
        rewardsOwed /= 1e18;

        return rewardsOwed;
    }

    /// @notice Get the user's current eligible balance.
    /// @dev Returns the user's eligible balance for the current epoch, not the next epoch
    function getUserEligibleBalance(address user) external view returns (uint128) {
        // Find the user's balance at the current epoch
        (, uint128 userBalance) = _findLatestBalanceUpdateForEpoch(currentEpoch, balanceUpdates[user]);
        return userBalance;
    }

    /// @notice Get the array of balance updates for a user.
    /// @param user The user to get the balance updates for.
    function getTotalBalanceUpdates(address user) public view returns (uint256) {
        return balanceUpdates[user].length;
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Mint shares
    /// @dev This function is overridden from the ERC20 implementation to account for incentives.
    function _mint(address to, uint256 amount) internal override(ERC20) {
        // Mint the shares to the depositor
        super._mint(to, amount);

        // Mark this deposit eligible for incentives earned from the next epoch onwards
        _increaseNextEpochParticipation(to, uint128(amount));
    }

    /// @notice Burn shares
    /// @dev This function is overridden from the ERC20 implementation to account for incentives.
    function _burn(address from, uint256 amount) internal override(ERC20) {
        // Burn the shares from the depositor
        super._burn(from, amount);

        // Mark this deposit eligible for incentives earned from the next epoch onwards
        _decreaseCurrentAndNextEpochParticipation(from, uint128(amount));
    }

    /// @dev Roll over to the next epoch.
    /// @dev Should be called on every boring vault rebalance.
    function _rollOverEpoch() internal {
        // Cache current epoch for gas savings
        uint48 currEpoch = currentEpoch++;

        // Get the current and next epoch data.
        Epoch storage currentEpochData = epochs[currEpoch];
        Epoch storage nextEpochData = epochs[++currEpoch];

        // Update the current epoch's end timestamp and the next epoch's start timestamp.
        currentEpochData.endTimestamp = uint64(block.timestamp);
        nextEpochData.startTimestamp = uint64(block.timestamp);

        // Update the eligible shares for the next epoch by rolling them over.
        nextEpochData.eligibleShares += currentEpochData.eligibleShares;

        // Emit event for epoch start
        emit EpochStarted(currEpoch, nextEpochData.eligibleShares, block.timestamp);
    }

    /// @notice Increase the user's share balance for the next epoch
    function _increaseNextEpochParticipation(address user, uint128 amount) internal {
        // Skip participation accounting if it has been disabled for this address
        if (addressToIsDisabled[user]) return;

        // Cache next epoch for gas op
        uint48 nextEpoch = currentEpoch + 1;

        // Handle updating the balance accounting for this user
        BalanceUpdate[] storage userBalanceUpdates = balanceUpdates[user];
        if (userBalanceUpdates.length == 0) {
            // If there are no balance updates, create a new one
            userBalanceUpdates.push(BalanceUpdate({epoch: nextEpoch, totalSharesBalance: amount}));
        } else {
            // Get the last balance update
            BalanceUpdate storage lastBalanceUpdate = userBalanceUpdates[userBalanceUpdates.length - 1];
            // Ensure no duplicate entries
            if (lastBalanceUpdate.epoch == nextEpoch) {
                // Handle case for multiple deposits into an epoch
                lastBalanceUpdate.totalSharesBalance += amount;
            } else {
                // Handle case for the first deposit for an epoch
                userBalanceUpdates.push(
                    BalanceUpdate({
                        epoch: nextEpoch,
                        // Add the specified amount to the last balance update's total shares
                        totalSharesBalance: (lastBalanceUpdate.totalSharesBalance + amount)
                    })
                );
            }
        }

        // Get the epoch data for the current epoch and next epoch (epoch to deposit for)
        Epoch storage epochData = epochs[nextEpoch];
        // Account for deposit for the specified epoch
        epochData.eligibleShares += amount;

        // Emit event for this deposit
        emit UserDepositedIntoEpoch(user, nextEpoch, amount);
    }

    /// @notice Decrease the user's share balance for the current epoch.
    /// @dev If the user has a deposit for the next epoch, it will withdraw as much as possible from the next epoch and the rest from the current.
    function _decreaseCurrentAndNextEpochParticipation(address user, uint128 amount) internal {
        // Skip participation accounting if it has been disabled for this address.
        if (addressToIsDisabled[user]) return;

        // Cache the current epoch, next epoch, and user's balance.
        uint48 currEpoch = currentEpoch;
        uint48 nextEpoch = currEpoch + 1;

        // Cache the user's balance updates and its length.
        BalanceUpdate[] storage userBalanceUpdates = balanceUpdates[user];
        uint256 balanceUpdatesLength = userBalanceUpdates.length;
        // It is assumed that len > 0 when withdrawing.
        BalanceUpdate storage lastBalanceUpdate = userBalanceUpdates[balanceUpdatesLength - 1];

        // CASE 1: No deposit for the next epoch.
        if (lastBalanceUpdate.epoch <= currEpoch) {
            // CASE 1.1: Last balance update is for the current epoch.
            if (lastBalanceUpdate.epoch == currEpoch) {
                // If already updated for the current epoch, just update the total shares to reflect the decrease.
                lastBalanceUpdate.totalSharesBalance -= amount;
                // CASE 1.2: Last balance update is a previous epoch. Create a new update to preserve order of updates.
            } else {
                // Append a new update for the current epoch.
                userBalanceUpdates.push(
                    BalanceUpdate({
                        epoch: currEpoch,
                        // Deduct the amount from the last balance update by the shares to decrease by
                        totalSharesBalance: (lastBalanceUpdate.totalSharesBalance - amount)
                    })
                );
            }
            // Account for withdrawal in the current epoch.
            epochs[currEpoch].eligibleShares -= amount;
            // Emit event for this withdrawal.
            emit UserWithdrawnFromEpoch(user, currEpoch, amount);
            return;
        }

        // CASE 2: Only deposit made is for the next epoch.
        if (balanceUpdatesLength == 1) {
            // If there is only one balance update, it has to be for the next epoch. Update it and adjust the epoch's eligible shares.
            lastBalanceUpdate.totalSharesBalance -= amount;
            epochs[nextEpoch].eligibleShares -= amount;
            // Emit event for this withdrawal
            emit UserWithdrawnFromEpoch(user, nextEpoch, amount);
            return;
        }

        // Get the second-to-last update.
        BalanceUpdate storage secondLastBalanceUpdate = userBalanceUpdates[balanceUpdatesLength - 2];
        // The amount deposited for the next epoch.
        uint128 amountDepositedIntoNextEpoch =
            lastBalanceUpdate.totalSharesBalance - secondLastBalanceUpdate.totalSharesBalance;

        // CASE 3: Deposit Made for the next epoch and the full withdrawal amount cannot be removed solely from the last update.
        if (amount > amountDepositedIntoNextEpoch) {
            // The amount deposited into the next epoch will be withdrawn completely in this case: amountDepositedIntoNextEpoch == amountToWithdrawFromNextEpoch.
            // The rest of the withdrawal amount will be deducted from the current epoch.
            uint128 amountToWithdrawFromCurrentEpoch = (amount - amountDepositedIntoNextEpoch);
            if (secondLastBalanceUpdate.epoch == currEpoch) {
                // Withdraw the amount left over from withdrawing from the next epoch from the current epoch.
                secondLastBalanceUpdate.totalSharesBalance -= amountToWithdrawFromCurrentEpoch;
                epochs[currEpoch].eligibleShares -= amountToWithdrawFromCurrentEpoch;
                // Withdraw the amount that was deposited into the next epoch completely
                epochs[nextEpoch].eligibleShares -= amountDepositedIntoNextEpoch;
                // Since the next epoch's deposit was completely cleared, we can pop the next epoch's (last) update off.
                userBalanceUpdates.pop();
            } else {
                // Update the last entry to be for the current epoch.
                lastBalanceUpdate.epoch = currEpoch;
                lastBalanceUpdate.totalSharesBalance -= amount;
                // Withdraw the amount to withdraw from the current epoch from total eligible shared.
                epochs[currEpoch].eligibleShares -= amountToWithdrawFromCurrentEpoch;
                // Withdraw the full amount deposited into the next epoch.
                epochs[nextEpoch].eligibleShares -= amountDepositedIntoNextEpoch;
            }
            // Emit event for the withdrawals.
            emit UserWithdrawnFromEpoch(user, nextEpoch, amountDepositedIntoNextEpoch);
            emit UserWithdrawnFromEpoch(user, currEpoch, amountToWithdrawFromCurrentEpoch);
            return;
            // CASE 4: The full amount can be withdrawn from the next epoch. Modify the next epoch (last) update.
        } else {
            lastBalanceUpdate.totalSharesBalance -= amount;
            epochs[nextEpoch].eligibleShares -= amount;
            // Emit event for this withdrawal.
            emit UserWithdrawnFromEpoch(user, nextEpoch, amount);
            return;
        }
    }

    /// @notice Claims rewards (specified as rewardIds) for the caller.
    /// @dev Should be permissionless in normal circumstances.
    /// @param rewardIds The rewardIds to claim rewards for.
    /// @param user The address of the user to claim rewards for.
    function _claimRewards(uint256[] calldata rewardIds, address user) internal {
        // Get the epoch range for all rewards to claim and the corresponding Reward structs.
        (uint48 minEpoch, uint48 maxEpoch, Reward[] memory rewardsToClaim) = _getEpochRangeForRewards(rewardIds, user);

        // Fetch the caller's balance update history.
        BalanceUpdate[] storage userBalanceUpdates = balanceUpdates[user];
        uint48 firstEpochDeposited = userBalanceUpdates[0].epoch;
        // Use the later of the minEpoch from rewards or the user's first deposit epoch.
        if (firstEpochDeposited > minEpoch) {
            minEpoch = firstEpochDeposited;
        }

        // Precompute the user's share ratios and epoch durations from minEpoch to maxEpoch.
        (uint256[] memory userShareRatios, uint256[] memory epochDurations) =
            _computeUserShareRatiosAndDurations(minEpoch, maxEpoch, userBalanceUpdates);

        // Get the rewards owed per unique token in rewardIds.
        (address[] memory uniqueTokens, uint256[] memory tokenAmounts) =
            _computeRewardsPerUniqueToken(rewardIds, rewardsToClaim, minEpoch, userShareRatios, epochDurations);

        // Transfer all reward tokens to the caller
        boringSafe.transfer(uniqueTokens, tokenAmounts, msg.sender);
    }

    /// @dev Retrieves the epoch range for a set of reward campaigns and marks them as claimed.
    /// @param rewardIds An array of reward campaign identifiers to process.
    /// @param user The user to mark these rewards as claimed for.
    /// @return minEpoch The smallest starting epoch among the rewards to claim.
    /// @return maxEpoch The largest ending epoch among the rewards to claim.
    /// @return rewardsToClaim An array of Reward structs corresponding to each reward ID provided.
    function _getEpochRangeForRewards(uint256[] calldata rewardIds, address user)
        internal
        returns (uint48 minEpoch, uint48 maxEpoch, Reward[] memory rewardsToClaim)
    {
        // Cache array length, rewards, and highest claimable rewardID for gas op.
        uint256 rewardsLength = rewardIds.length;
        // Check that the user is claiming at least 1 reward.
        if (rewardsLength == 0) {
            revert MustClaimAtLeastOneReward();
        }
        rewardsToClaim = new Reward[](rewardsLength);
        uint256 highestClaimaibleRewardId = maxRewardId - 1;

        // Initialize epoch range
        minEpoch = type(uint48).max;
        maxEpoch = 0;

        // Variables to cache reward claim data as a gas optimization.
        uint256 cachedRewardBucket;
        uint256 cachedClaimedRewards;

        // Variables used to preprocess rewardsIds to get a range of epochs for all rewards and mark them as claimed.
        for (uint256 i = 0; i < rewardsLength; ++i) {
            // Check if this rewardID exists
            if (rewardIds[i] > highestClaimaibleRewardId) {
                revert CannotClaimFutureReward();
            }

            // Cache management (reading and writing)
            {
                // Determine the reward bucket that this rewardId belongs in.
                uint256 rewardBucket = rewardIds[i] / 256;

                if (i == 0) {
                    // Initialize cache with the reward bucket and bit field
                    cachedRewardBucket = rewardBucket;
                    cachedClaimedRewards = userToRewardBucketToClaimedRewards[user][rewardBucket];
                } else if (cachedRewardBucket != rewardBucket) {
                    // Write back the cached claim data to persistent storage
                    userToRewardBucketToClaimedRewards[user][cachedRewardBucket] = cachedClaimedRewards;
                    // Updated cache with the new reward bucket and rewards bit field
                    cachedRewardBucket = rewardBucket;
                    cachedClaimedRewards = userToRewardBucketToClaimedRewards[user][rewardBucket];
                }

                // The bit offset for rewardId within that bucket
                uint256 bitOffset = rewardIds[i] % 256;

                // Shift right so that the target bit is in the least significant position,
                // then check if it's 1 (indicating that it has been claimed)
                bool claimed = ((cachedClaimedRewards >> bitOffset) & 1) == 1;
                if (claimed) {
                    // If the user has already claimed this reward, revert.
                    revert RewardClaimedAlready(rewardIds[i]);
                } else {
                    // If user hasn't claimed this reward
                    // Set the bit corresponding to rewardId to true - indicating it has been claimed
                    cachedClaimedRewards |= (1 << bitOffset);
                }
            }

            // Retrieve and cache the reward ID, start epoch, and end epoch.
            rewardsToClaim[i] = rewards[rewardIds[i]];
            if (rewardsToClaim[i].startEpoch < minEpoch) {
                minEpoch = rewardsToClaim[i].startEpoch;
            }
            if (rewardsToClaim[i].endEpoch > maxEpoch) {
                maxEpoch = rewardsToClaim[i].endEpoch;
            }
        }
        // Write back the final cache to persistent storage
        userToRewardBucketToClaimedRewards[user][cachedRewardBucket] = cachedClaimedRewards;
    }

    /// @dev Computes the user's share ratios and epoch durations for every epoch between minEpoch and maxEpoch.
    /// @param minEpoch The starting epoch.
    /// @param maxEpoch The ending epoch.
    /// @param userBalanceUpdates The user's balance update history.
    /// @return userShareRatios An array of the user’s fraction of shares for each epoch.
    /// @return epochDurations An array of the epoch durations (endTimestamp - startTimestamp) for each epoch.
    function _computeUserShareRatiosAndDurations(
        uint48 minEpoch,
        uint48 maxEpoch,
        BalanceUpdate[] storage userBalanceUpdates
    ) internal view returns (uint256[] memory userShareRatios, uint256[] memory epochDurations) {
        uint256 epochCount = maxEpoch - minEpoch + 1;
        userShareRatios = new uint256[](epochCount);
        epochDurations = new uint256[](epochCount);

        uint256 userBalanceUpdatesLength = userBalanceUpdates.length;
        // Get the user's share balance at minEpoch.
        (uint256 balanceIndex, uint256 epochSharesBalance) =
            _findLatestBalanceUpdateForEpoch(minEpoch, userBalanceUpdates);
        // Cache the next balance update if it exists.
        bool hasNextUpdate = balanceIndex < userBalanceUpdatesLength - 1;
        BalanceUpdate memory nextUserBalanceUpdate;
        if (hasNextUpdate) {
            nextUserBalanceUpdate = userBalanceUpdates[balanceIndex + 1];
        }

        // Loop over each epoch from minEpoch to maxEpoch.
        for (uint48 epoch = minEpoch; epoch <= maxEpoch; ++epoch) {
            // Update the user's share balance if a new balance update occurs at the current epoch.
            if (epoch == nextUserBalanceUpdate.epoch && hasNextUpdate) {
                epochSharesBalance = nextUserBalanceUpdate.totalSharesBalance;
                hasNextUpdate = ++balanceIndex < userBalanceUpdatesLength - 1;
                if (hasNextUpdate) {
                    nextUserBalanceUpdate = userBalanceUpdates[balanceIndex + 1];
                }
            }

            // Retrieve the epoch data.
            Epoch storage epochData = epochs[epoch];
            uint128 eligibleShares = epochData.eligibleShares;
            // Only calculate ratio and duration if there are eligible shares. Else leave those set to 0.
            if (eligibleShares != 0) {
                uint256 epochIndex = epoch - minEpoch;
                // Calculate the user's fraction of shares for this epoch.
                userShareRatios[epochIndex] = epochSharesBalance.divWadDown(eligibleShares);
                // Calculate the epoch duration.
                epochDurations[epochIndex] = epochData.endTimestamp - epochData.startTimestamp;
            }
        }
    }

    /// @notice Computes the rewards per unique token for a set of reward campaigns.
    /// @param rewardIds An array of identifiers for the reward campaigns to process.
    /// @param rewardsToClaim An array of Reward structs containing the details for each reward campaign.
    /// @param minEpoch The starting epoch from which rewards are calculated, adjusted to be the
    ///        later of the reward campaign's minimum epoch or the user's first deposit epoch.
    /// @param userShareRatios An array of precomputed share ratios for the user over a range of epochs,
    ///        used to determine the user's portion of the rewards.
    /// @param epochDurations An array of epoch durations corresponding to the time intervals over which
    ///        rewards are computed.
    /// @return uniqueTokens An array of unique token addresses for which rewards have been computed.
    /// @return tokenAmounts An array of reward amounts corresponding to each unique token in `uniqueTokens`.
    function _computeRewardsPerUniqueToken(
        uint256[] calldata rewardIds,
        Reward[] memory rewardsToClaim,
        uint48 minEpoch,
        uint256[] memory userShareRatios,
        uint256[] memory epochDurations
    ) internal returns (address[] memory uniqueTokens, uint256[] memory tokenAmounts) {
        uint256 numRewards = rewardIds.length;
        uniqueTokens = new address[](numRewards);
        tokenAmounts = new uint256[](numRewards);
        uint256 uniqueCount = 0;

        // For each reward campaign, calculate the rewards owed and accumulate by token.
        for (uint256 i = 0; i < numRewards; ++i) {
            uint256 rewardsOwed = _calculateRewardsOwed(rewardsToClaim[i], minEpoch, userShareRatios, epochDurations);

            if (rewardsOwed > 0) {
                // Check if this reward token was already encountered.
                bool found = false;
                for (uint256 j = 0; j < uniqueCount; ++j) {
                    if (uniqueTokens[j] == rewardsToClaim[i].token) {
                        tokenAmounts[j] += rewardsOwed;
                        found = true;
                        break;
                    }
                }
                // If not found, add a new entry.
                if (!found) {
                    uniqueTokens[uniqueCount] = rewardsToClaim[i].token;
                    tokenAmounts[uniqueCount] = rewardsOwed;
                    uniqueCount++;
                }
                // Emit the reward-claim event per reward campaign.
                emit UserRewardsClaimed(msg.sender, rewardsToClaim[i].token, rewardIds[i], rewardsOwed);
            }
        }

        // Resize the arrays to the actual number of unique tokens if needed
        if (uniqueCount < numRewards) {
            assembly ("memory-safe") {
                mstore(uniqueTokens, uniqueCount)
                mstore(tokenAmounts, uniqueCount)
            }
        }
    }

    /// @dev Calculates the total rewards owed for a single reward campaign over its epoch range.
    /// @param reward The reward campaign data.
    /// @param minEpoch The minimum epoch to consider (used as the offset for precomputed arrays).
    /// @param userShareRatios An array of the user's share ratios for each epoch.
    /// @param epochDurations An array of epoch durations for each epoch.
    /// @return rewardsOwed The total amount of tokens owed for this reward.
    function _calculateRewardsOwed(
        Reward memory reward,
        uint128 minEpoch,
        uint256[] memory userShareRatios,
        uint256[] memory epochDurations
    ) internal pure returns (uint256 rewardsOwed) {
        // Scale rate up by 1e18 to avoid precision loss
        uint256 rateWAD = reward.rewardRate * 1e18;
        for (uint256 epoch = reward.startEpoch; epoch <= reward.endEpoch; ++epoch) {
            if (epoch < minEpoch) {
                // If user didn't have a deposit in this epoch, skip reward calculation
                continue;
            }
            uint256 epochIndex = epoch - minEpoch;
            uint256 userShareRatioForEpoch = userShareRatios[epochIndex];
            // Only process epochs where the user had a positive share ratio.
            if (userShareRatioForEpoch > 0) {
                uint256 epochReward = rateWAD.mulWadDown(epochDurations[epochIndex]);
                rewardsOwed += epochReward.mulWadDown(userShareRatioForEpoch);
            }
        }
        // Scale down by 1e18 to get the final reward amount
        rewardsOwed /= 1e18;
    }

    /// @notice Find the latest balance update index and balance for the specified epoch.
    /// @dev Assumes `balanceUpdates` is sorted in ascending order by `epoch`.
    /// @param epoch The epoch for which we want the user's balance.
    /// @param userBalanceUpdates The historical balance updates for a user, sorted ascending by epoch.
    /// @return The latest balance update index and balance for the given epoch.
    function _findLatestBalanceUpdateForEpoch(uint48 epoch, BalanceUpdate[] storage userBalanceUpdates)
        internal
        view
        returns (uint256, uint128)
    {
        // No balance changes at all
        if (userBalanceUpdates.length == 0) {
            return (0, 0);
        }

        // If the requested epoch is before the first recorded epoch,
        // assume the user had 0 shares.
        if (epoch < userBalanceUpdates[0].epoch) {
            return (epoch, 0);
        }

        // If the requested epoch is beyond the last recorded epoch,
        // return the most recent known balance.
        uint256 lastIndex = userBalanceUpdates.length - 1;
        if (epoch >= userBalanceUpdates[lastIndex].epoch) {
            return (lastIndex, userBalanceUpdates[lastIndex].totalSharesBalance);
        }

        // Standard binary search:
        // We want the highest index where balanceUpdates[index].epoch <= epoch
        uint256 low = 0;
        uint256 high = lastIndex;

        // Perform the binary search in the range [low, high]
        while (low < high) {
            // Midpoint (biased towards the higher index when (low+high) is even)
            uint256 mid = (low + high + 1) >> 1; // same as (low + high + 1) / 2

            if (userBalanceUpdates[mid].epoch <= epoch) {
                // If mid's epoch is <= target, we move `low` up to mid
                low = mid;
            } else {
                // If mid's epoch is > target, we move `high` down to mid - 1
                high = mid - 1;
            }
        }

        // Now `low == high`, which should be the index where epoch <= balanceUpdates[low].epoch
        // and balanceUpdates[low].epoch is the largest epoch not exceeding `epoch`.
        return (low, userBalanceUpdates[low].totalSharesBalance);
    }
}

File 11 of 17 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

File 12 of 17 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 13 of 17 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 14 of 17 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Interface that must be implemented by smart contracts in order to receive
 * ERC-1155 token transfers.
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC-1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC-1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 15 of 17 : BoringSafe.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

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

/// @title BoringSafe
/// @notice Lightweight middleware contract for holding funds that have been committed to reward campaigns in BoringChef.
contract BoringSafe is Owned(msg.sender) {
    using SafeTransferLib for ERC20;

    error ArrayLengthMismatch();

    /// @notice Transfers tokens from this contract.
    /// @notice Only callable by the owner (BoringChef).
    /// @param tokens The addresses of the ERC20 tokens to transfer.
    /// @param amounts The amounts of each token to transfer.
    /// @param to The recipient address.
    function transfer(address[] memory tokens, uint256[] memory amounts, address to) external onlyOwner {
        // Make sure each token has a corresponding amount
        uint256 numTokens = tokens.length;
        if (numTokens != amounts.length) {
            revert ArrayLengthMismatch();
        }
        // Transfer all tokens to the specified address
        for (uint256 i = 0; i < numTokens; ++i) {
            // Transfer ERC20 tokens
            ERC20(tokens[i]).safeTransfer(to, amounts[i]);
        }
    }
}

File 16 of 17 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

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

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

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

    address public owner;

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

        _;
    }

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

    constructor(address _owner) {
        owner = _owner;

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

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

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

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

Settings
{
  "remappings": [
    "@solmate/=lib/solmate/src/",
    "@forge-std/=lib/forge-std/src/",
    "@ds-test/=lib/forge-std/lib/ds-test/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@ccip/=lib/ccip/",
    "@oapp-auth/=lib/OAppAuth/src/",
    "@devtools-oapp-evm/=lib/OAppAuth/lib/devtools/packages/oapp-evm/contracts/oapp/",
    "@layerzerolabs/lz-evm-messagelib-v2/=lib/OAppAuth/node_modules/@layerzerolabs/lz-evm-messagelib-v2/",
    "@layerzerolabs/lz-evm-protocol-v2/=lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/protocol/",
    "@layerzerolabs/oapp-evm/=lib/OAppAuth/lib/devtools/packages/oapp-evm/",
    "@lz-oapp-evm/=lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/oapp/contracts/oapp/",
    "@sbu/=lib/OAppAuth/lib/solidity-bytes-utils/",
    "LayerZero-V2/=lib/OAppAuth/lib/",
    "OAppAuth/=lib/OAppAuth/",
    "ccip/=lib/ccip/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solidity-bytes-utils/=lib/OAppAuth/node_modules/solidity-bytes-utils/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotClaimFutureReward","type":"error"},{"inputs":[],"name":"CannotDisableRewardAccrualMoreThanOnce","type":"error"},{"inputs":[],"name":"CannotEnableRewardAccrualMoreThanOnce","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidRewardCampaignDuration","type":"error"},{"inputs":[],"name":"MustClaimAtLeastOneReward","type":"error"},{"inputs":[],"name":"NoFutureEpochRewards","type":"error"},{"inputs":[],"name":"OnlyClaimant","type":"error"},{"inputs":[{"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"RewardClaimedAlready","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Enter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"eligibleShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTimestamp","type":"uint256"}],"name":"EpochStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"startEpoch","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"endEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"RewardsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"UserDepositedIntoEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UserRewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"UserWithdrawnFromEpoch","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"addressToClaimant","outputs":[{"internalType":"address","name":"claimant","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"addressToIsDisabled","outputs":[{"internalType":"bool","name":"isDisabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"claimant","type":"address"}],"name":"assignClaimantForUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"balanceUpdates","outputs":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"uint128","name":"totalSharesBalance","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boringSafe","outputs":[{"internalType":"contract BoringSafe","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"rewardIds","type":"uint256[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"rewardIds","type":"uint256[]"},{"internalType":"address","name":"user","type":"address"}],"name":"claimRewardsOnBehalfOfUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"disableRewardAccrual","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint48[]","name":"startEpochs","type":"uint48[]"},{"internalType":"uint48[]","name":"endEpochs","type":"uint48[]"}],"name":"distributeRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"enableRewardAccrual","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"contract ERC20","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"enter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"","type":"uint48"}],"name":"epochs","outputs":[{"internalType":"uint128","name":"eligibleShares","type":"uint128"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"contract ERC20","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getBalanceUpdates","outputs":[{"components":[{"internalType":"uint48","name":"epoch","type":"uint48"},{"internalType":"uint128","name":"totalSharesBalance","type":"uint128"}],"internalType":"struct BoringChef.BalanceUpdate[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getTotalBalanceUpdates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserEligibleBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"getUserRewardBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hook","outputs":[{"internalType":"contract BeforeTransferHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"manage","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"manage","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxRewardId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"uint48","name":"startEpoch","type":"uint48"},{"internalType":"uint48","name":"endEpoch","type":"uint48"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"rewardRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rollOverEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_hook","type":"address"}],"name":"setBeforeTransferHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"rewardBucket","type":"uint256"}],"name":"userToRewardBucketToClaimedRewards","outputs":[{"internalType":"uint256","name":"claimedRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

61010060405234801562000011575f80fd5b50604051620052a5380380620052a58339810160408190526200003491620002b3565b5f80546001600160a01b0386166001600160a01b0319918216811783556001805490921690915560405186928692869286928592859285928992909133907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908490a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a35060029050620000dd8482620003dc565b506003620000ec8382620003dc565b5060ff81166080524660a052620001026200014c565b60c0525050604051620001169150620001e7565b604051809103905ff08015801562000130573d5f803e3d5ffd5b506001600160a01b031660e052506200051e9650505050505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60026040516200017f9190620004a4565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b61053a8062004d6b83390190565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000219575f80fd5b81516001600160401b0380821115620002365762000236620001f5565b604051601f8301601f19908116603f01168101908282118183101715620002615762000261620001f5565b816040528381526020925086838588010111156200027d575f80fd5b5f91505b83821015620002a0578582018301518183018401529082019062000281565b5f93810190920192909252949350505050565b5f805f8060808587031215620002c7575f80fd5b84516001600160a01b0381168114620002de575f80fd5b60208601519094506001600160401b0380821115620002fb575f80fd5b620003098883890162000209565b945060408701519150808211156200031f575f80fd5b506200032e8782880162000209565b925050606085015160ff8116811462000345575f80fd5b939692955090935050565b600181811c908216806200036557607f821691505b6020821081036200038457634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620003d7575f81815260208120601f850160051c81016020861015620003b25750805b601f850160051c820191505b81811015620003d357828155600101620003be565b5050505b505050565b81516001600160401b03811115620003f857620003f8620001f5565b620004108162000409845462000350565b846200038a565b602080601f83116001811462000446575f84156200042e5750858301515b5f19600386901b1c1916600185901b178555620003d3565b5f85815260208120601f198616915b82811015620004765788860151825594840194600190910190840162000455565b50858210156200049457878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b5f808354620004b38162000350565b60018281168015620004ce5760018114620004e45762000512565b60ff198416875282151583028701945062000512565b875f526020805f205f5b85811015620005095781548a820152908401908201620004ee565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161480b620005605f395f818161077c0152818161129e015261234901525f610e6301525f610e2e01525f610442015261480b5ff3fe608060405260043610610278575f3560e01c80637a9e5e4b1161014a578063b7ba9fa2116100be578063dd62ed3e11610078578063dd62ed3e146108f9578063f23a6e611461092f578063f2fde38b1461095a578063f301af4214610979578063f549794014610a0b578063f6e715d014610a2a575f80fd5b8063b7ba9fa21461081b578063b82148141461083a578063bc197c8114610871578063bf7e214f1461089c578063c7ad155a146108bb578063d505accf146108da575f80fd5b80638da5cb5b1161010f5780638da5cb5b1461074d5780639579b6891461076b57806395d89b411461079e5780639c0fed1f146107b2578063a887b71c146107e8578063a9059cbb146107fc575f80fd5b80637a9e5e4b146106915780637d2066e6146106b05780637ecebe00146106e45780637f5a7c7b1461070f5780638929565f1461072e575f80fd5b80633644e515116101ec5780635c2ff63b116101a65780635c2ff63b1461055f5780635eac62391461057457806366c6822c1461059357806370a0823114610612578063766718081461063d578063773caf4114610672575f80fd5b80633644e51514610476578063369874101461048a57806339d6ba32146104d657806354ee9b52146104f5578063556c6f68146105145780635a795d6614610540575f80fd5b806318160ddd1161023d57806318160ddd1461037457806318457e6114610397578063224d8703146103b857806323b872dd146103e45780632fd867b114610403578063313ce56714610431575f80fd5b806301ffc9a71461028357806306fdde03146102b7578063095ea7b3146102d85780630e9de728146102f7578063150b7a021461033c575f80fd5b3661027f57005b5f80fd5b34801561028e575f80fd5b506102a261029d366004613b88565b610a49565b60405190151581526020015b60405180910390f35b3480156102c2575f80fd5b506102cb610a7f565b6040516102ae9190613bfc565b3480156102e3575f80fd5b506102a26102f2366004613c22565b610b0b565b348015610302575f80fd5b50610316610311366004613c22565b610b76565b6040805165ffffffffffff90931683526001600160801b039091166020830152016102ae565b348015610347575f80fd5b5061035b610356366004613cfb565b610bba565b6040516001600160e01b031990911681526020016102ae565b34801561037f575f80fd5b5061038960045481565b6040519081526020016102ae565b3480156103a2575f80fd5b506103b66103b1366004613d62565b610bcb565b005b3480156103c3575f80fd5b506103d76103d2366004613df9565b610c90565b6040516102ae9190613e8b565b3480156103ef575f80fd5b506102a26103fe366004613eeb565b610e0b565b34801561040e575f80fd5b506102a261041d366004613f29565b600d6020525f908152604090205460ff1681565b34801561043c575f80fd5b506104647f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102ae565b348015610481575f80fd5b50610389610e2b565b348015610495575f80fd5b506104be6104a4366004613f29565b600e6020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102ae565b3480156104e1575f80fd5b506103b66104f0366004613d62565b610e85565b348015610500575f80fd5b506103b661050f366004613f44565b610f33565b34801561051f575f80fd5b5061053361052e366004613f29565b611423565b6040516102ae9190613ffe565b34801561054b575f80fd5b506103b661055a36600461405d565b6114b2565b34801561056a575f80fd5b50610389600c5481565b34801561057f575f80fd5b506103b661058e366004614094565b611510565b34801561059e575f80fd5b506105e36105ad3660046140d2565b60096020525f90815260409020546001600160801b038116906001600160401b03600160801b8204811691600160c01b90041683565b604080516001600160801b0390941684526001600160401b0392831660208501529116908201526060016102ae565b34801561061d575f80fd5b5061038961062c366004613f29565b60056020525f908152604090205481565b348015610648575f80fd5b5060085461065b9065ffffffffffff1681565b60405165ffffffffffff90911681526020016102ae565b34801561067d575f80fd5b506103b661068c366004613f29565b611550565b34801561069c575f80fd5b506103b66106ab366004613f29565b6115f5565b3480156106bb575f80fd5b506103896106ca366004613f29565b6001600160a01b03165f908152600a602052604090205490565b3480156106ef575f80fd5b506103896106fe366004613f29565b60076020525f908152604090205481565b34801561071a575f80fd5b506010546104be906001600160a01b031681565b348015610739575f80fd5b506103b6610748366004613f29565b6116d9565b348015610758575f80fd5b505f546104be906001600160a01b031681565b348015610776575f80fd5b506104be7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107a9575f80fd5b506102cb61172c565b3480156107bd575f80fd5b506103896107cc366004613c22565b600f60209081525f928352604080842090915290825290205481565b3480156107f3575f80fd5b506103b6611739565b348015610807575f80fd5b506102a2610816366004613c22565b611774565b348015610826575f80fd5b506103b66108353660046140f7565b611789565b348015610845575f80fd5b50610859610854366004613f29565b611803565b6040516001600160801b0390911681526020016102ae565b34801561087c575f80fd5b5061035b61088b3660046141ba565b63bc197c8160e01b95945050505050565b3480156108a7575f80fd5b506001546104be906001600160a01b031681565b3480156108c6575f80fd5b506103b66108d5366004613f29565b611833565b3480156108e5575f80fd5b506103b66108f4366004614260565b6118e7565b348015610904575f80fd5b5061038961091336600461405d565b600660209081525f928352604080842090915290825290205481565b34801561093a575f80fd5b5061035b6109493660046142d1565b63f23a6e6160e01b95945050505050565b348015610965575f80fd5b506103b6610974366004613f29565b611b26565b348015610984575f80fd5b506109d3610993366004614334565b600b6020525f90815260409020805460019091015465ffffffffffff80831692600160301b810490911691600160601b9091046001600160a01b03169084565b6040805165ffffffffffff95861681529490931660208501526001600160a01b039091169183019190915260608201526080016102ae565b348015610a16575f80fd5b50610389610a25366004613c22565b611ba1565b348015610a35575f80fd5b506102cb610a4436600461434b565b611ce5565b5f6001600160e01b03198216630271189760e51b1480610a7957506301ffc9a760e01b6001600160e01b03198316145b92915050565b60028054610a8c906143ce565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab8906143ce565b8015610b035780601f10610ada57610100808354040283529160200191610b03565b820191905f5260205f20905b815481529060010190602001808311610ae657829003601f168201915b505050505081565b335f8181526006602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610b659086815260200190565b60405180910390a350600192915050565b600a602052815f5260405f208181548110610b8f575f80fd5b5f9182526020909120015465ffffffffffff81169250600160301b90046001600160801b0316905082565b630a85bd0160e11b5b949350505050565b610be0335f356001600160e01b031916611d73565b610c055760405162461bcd60e51b8152600401610bfc90614406565b60405180910390fd5b610c0f8282611e18565b8215610c2957610c296001600160a01b0385168685611e2c565b816001600160a01b0316846001600160a01b0316866001600160a01b03167fe0c82280a1164680e0cf43be7db4c4c9f985423623ad7a544fb76c772bdc60438685604051610c81929190918252602082015260400190565b60405180910390a45050505050565b6060610ca7335f356001600160e01b031916611d73565b610cc35760405162461bcd60e51b8152600401610bfc90614406565b610ccb611ebb565b85806001600160401b03811115610ce457610ce4613c4c565b604051908082528060200260200182016040528015610d1757816020015b6060815260200190600190039081610d025790505b5091505f5b81811015610dff57610dd1878783818110610d3957610d3961442c565b9050602002810190610d4b9190614440565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150859050818110610d9357610d9361442c565b905060200201358b8b85818110610dac57610dac61442c565b9050602002016020810190610dc19190613f29565b6001600160a01b03169190612003565b838281518110610de357610de361442c565b602002602001018190525080610df890614496565b9050610d1c565b50509695505050505050565b5f610e1684846120a3565b610e2184848461211f565b90505b9392505050565b5f7f00000000000000000000000000000000000000000000000000000000000000004614610e6057610e5b612141565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b610e9a335f356001600160e01b031916611d73565b610eb65760405162461bcd60e51b8152600401610bfc90614406565b8215610ed157610ed16001600160a01b0385168630866121da565b610edb828261227e565b816001600160a01b0316846001600160a01b0316866001600160a01b03167fea00f88768a86184a6e515238a549c171769fe7460a011d6fd0bcd48ca078ea48685604051610c81929190918252602082015260400190565b610f48335f356001600160e01b031916611d73565b610f645760405162461bcd60e51b8152600401610bfc90614406565b868581141580610f745750808414155b80610f7f5750808214155b15610f9d5760405163512509d360e11b815260040160405180910390fd5b5f5b8181101561141757838382818110610fb957610fb961442c565b9050602002016020810190610fce91906140d2565b65ffffffffffff16868683818110610fe857610fe861442c565b9050602002016020810190610ffd91906140d2565b65ffffffffffff16111561102457604051630c2b941b60e41b815260040160405180910390fd5b60085465ffffffffffff168484838181106110415761104161442c565b905060200201602081019061105691906140d2565b65ffffffffffff161061107c5760405163383ed2a960e01b815260040160405180910390fd5b5f60095f8888858181106110925761109261442c565b90506020020160208101906110a791906140d2565b65ffffffffffff1665ffffffffffff1681526020019081526020015f2090505f60095f8787868181106110dc576110dc61442c565b90506020020160208101906110f191906140d2565b65ffffffffffff1665ffffffffffff1681526020019081526020015f209050604051806080016040528089898681811061112d5761112d61442c565b905060200201602081019061114291906140d2565b65ffffffffffff1681526020018787868181106111615761116161442c565b905060200201602081019061117691906140d2565b65ffffffffffff1681526020018d8d868181106111955761119561442c565b90506020020160208101906111aa9190613f29565b6001600160a01b0316815283548354602090920191611218916111e8916001600160401b03600160801b909204821691600160c01b909104166144ae565b6001600160401b03168d8d888181106112035761120361442c565b9050602002013561229290919063ffffffff16565b9052600c545f908152600b602090815260409182902083518154928501519385015165ffffffffffff9182166bffffffffffffffffffffffff1990941693909317600160301b9190941602929092176bffffffffffffffffffffffff16600160601b6001600160a01b039092169190910217815560609091015160019091015561130e337f00000000000000000000000000000000000000000000000000000000000000008c8c878181106112cf576112cf61442c565b905060200201358f8f888181106112e8576112e861442c565b90506020020160208101906112fd9190613f29565b6001600160a01b03169291906121da565b8585848181106113205761132061442c565b905060200201602081019061133591906140d2565b65ffffffffffff1688888581811061134f5761134f61442c565b905060200201602081019061136491906140d2565b65ffffffffffff168d8d8681811061137e5761137e61442c565b90506020020160208101906113939190613f29565b6001600160a01b03167fdb2ae7fd4c873ab5f72ed0c2a86f8403da7fca4a50110ef5381f6c365829322d8d8d888181106113cf576113cf61442c565b90506020020135600c5f8154809291906113e890614496565b909155506040805192835260208301919091520160405180910390a450508061141090614496565b9050610f9f565b50505050505050505050565b6001600160a01b0381165f908152600a60209081526040808320805482518185028101850190935280835260609492939192909184015b828210156114a7575f848152602090819020604080518082019091529084015465ffffffffffff81168252600160301b90046001600160801b03168183015282526001909201910161145a565b505050509050919050565b6114c7335f356001600160e01b031916611d73565b6114e35760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b039182165f908152600e6020526040902080546001600160a01b03191691909216179055565b611525335f356001600160e01b031916611d73565b6115415760405162461bcd60e51b8152600401610bfc90614406565b61154c8282336122a6565b5050565b611565335f356001600160e01b031916611d73565b6115815760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b0381165f908152600d602052604081205460ff16151590036115bd5760405163e58d4f7960e01b815260040160405180910390fd5b6001600160a01b0381165f908152600d60209081526040808320805460ff1916905560059091529020546115f29082906123bd565b50565b5f546001600160a01b0316331480611686575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061164790339030906001600160e01b03195f3516906004016144d5565b602060405180830381865afa158015611662573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116869190614502565b61168e575f80fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a350565b6116ee335f356001600160e01b031916611d73565b61170a5760405162461bcd60e51b8152600401610bfc90614406565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b60038054610a8c906143ce565b61174e335f356001600160e01b031916611d73565b61176a5760405162461bcd60e51b8152600401610bfc90614406565b611772611ebb565b565b5f61177f33846120a3565b610e248383612658565b61179e335f356001600160e01b031916611d73565b6117ba5760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b038181165f908152600e60205260409020541633146117f357604051632070af6360e01b815260040160405180910390fd5b6117fe8383836122a6565b505050565b6008546001600160a01b0382165f908152600a6020526040812090918291610bc39165ffffffffffff1690612679565b611848335f356001600160e01b031916611d73565b6118645760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b0381165f908152600d602052604090205460ff1615156001036118a157604051632ebc8f1160e11b815260040160405180910390fd5b6001600160a01b0381165f908152600560205260409020546118c49082906127ff565b6001600160a01b03165f908152600d60205260409020805460ff19166001179055565b428410156119375760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610bfc565b5f6001611942610e2b565b6001600160a01b038a81165f8181526007602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611a4a573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811615801590611a805750876001600160a01b0316816001600160a01b0316145b611abd5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610bfc565b6001600160a01b039081165f9081526006602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a350505050505050565b611b3b335f356001600160e01b031916611d73565b611b575760405162461bcd60e51b8152600401610bfc90614406565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b6001600160a01b0382165f908152600a60209081526040808320848452600b9092528220600181015483908190611be090670de0b6b3a7640000614521565b835490915065ffffffffffff165b835465ffffffffffff600160301b909104811690821611611cc7575f611c148287612679565b9150506001600160801b03811615611cb45765ffffffffffff82165f9081526009602052604081208054909190611c57906001600160801b038581169116612292565b82549091505f90611c81906001600160401b03600160801b8204811691600160c01b9004166144ae565b6001600160401b031690505f611c978783612edf565b9050611ca38184612edf565b611cad9089614538565b9750505050505b5080611cbf8161454b565b915050611bee565b50611cda670de0b6b3a764000083614583565b979650505050505050565b6060611cfc335f356001600160e01b031916611d73565b611d185760405162461bcd60e51b8152600401610bfc90614406565b611d20611ebb565b611d6a84848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050506001600160a01b03881691905084612003565b95945050505050565b6001545f906001600160a01b03168015801590611dfa575060405163b700961360e01b81526001600160a01b0382169063b700961390611dbb908790309088906004016144d5565b602060405180830381865afa158015611dd6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dfa9190614502565b80610bc357505f546001600160a01b03858116911614949350505050565b611e228282612ef3565b61154c82826127ff565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f511416151615611e765750823b153d17155b80611eb55760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610bfc565b50505050565b600880545f9165ffffffffffff9091169082611ed68361454b565b82546101009290920a65ffffffffffff81810219909316918316021790915581165f908152600960208190526040822092935081611f138561454b565b65ffffffffffff8116825260208201929092526040015f90812084546001600160401b034216600160c01b81026001600160c01b039092169190911786558154600160801b90910267ffffffffffffffff60801b1982168117835586549497509194506001600160801b0393841693859392611f96928692918216911617614596565b82546101009290920a6001600160801b0381810219909316918316021790915582546040805191909216815242602082015265ffffffffffff861692507f29db3deb62ef2036e5eb93aad68d2362aec0711af592cb365566603bd88651d4910160405180910390a2505050565b60608147101561202f5760405163cf47918160e01b815247600482015260248101839052604401610bfc565b5f80856001600160a01b0316848660405161204a91906145b6565b5f6040518083038185875af1925050503d805f8114612084576040519150601f19603f3d011682016040523d82523d5f602084013e612089565b606091505b5091509150612099868383612f5a565b9695505050505050565b6010546001600160a01b03161561154c57601054604051630abd626b60e41b81526001600160a01b03848116600483015283811660248301523360448301529091169063abd626b0906064015f6040518083038186803b158015612105575f80fd5b505afa158015612117573d5f803e3d5ffd5b505050505050565b5f61212b848484612fb6565b905061213784836127ff565b610e2483836123bd565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600260405161217291906145d1565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af191505080601f3d1160015f5114161516156122335750833b153d17155b806122775760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bfc565b5050505050565b6122888282613090565b61154c82826123bd565b5f610e2483670de0b6b3a7640000846130df565b5f805f6122b48686866130fa565b6001600160a01b0387165f908152600a6020526040812080549497509295509093509091829082906122e8576122e861442c565b5f9182526020909120015465ffffffffffff9081169150851681111561230c578094505b5f8061231987878661347e565b915091505f8061232d8c8c898c8888613762565b604051631d664b4f60e21b815291935091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906375992d3c906123829085908590339060040161466d565b5f604051808303815f87803b158015612399575f80fd5b505af11580156123ab573d5f803e3d5ffd5b50505050505050505050505050505050565b6001600160a01b0382165f908152600d602052604090205460ff16156123e1575050565b6008545f906123f99065ffffffffffff1660016146fd565b6001600160a01b0384165f908152600a602052604081208054929350919003612482576040805180820190915265ffffffffffff80841682526001600160801b03808616602080850191825285546001810187555f878152919091209451940180549151909216600160301b026001600160b01b031990911693909216929092171790556125a1565b80545f9082906124949060019061471c565b815481106124a4576124a461442c565b5f918252602090912001805490915065ffffffffffff808516911603612511578054849082906006906124e8908490600160301b90046001600160801b0316614596565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061259f565b8160405180604001604052808565ffffffffffff16815260200186845f0160069054906101000a90046001600160801b031661254d9190614596565b6001600160801b0390811690915282546001810184555f9384526020938490208351910180549490930151909116600160301b026001600160b01b031990931665ffffffffffff909116179190911790555b505b65ffffffffffff82165f9081526009602052604081208054909185918391906125d49084906001600160801b0316614596565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508265ffffffffffff16856001600160a01b03167ff618eaac43bda1b7a8ca1344c0d6084f93d0b08e7beb0554b03ba72c4ca72bac8660405161264991906001600160801b0391909116815260200190565b60405180910390a35050505050565b5f6126638383613a09565b905061266f33836127ff565b610a7983836123bd565b80545f908190810361268f57505f9050806127f8565b825f815481106126a1576126a161442c565b5f9182526020909120015465ffffffffffff90811690851610156126d057505065ffffffffffff82165f6127f8565b82545f906126e09060019061471c565b90508381815481106126f4576126f461442c565b5f9182526020909120015465ffffffffffff9081169086161061274b57808482815481106127245761272461442c565b5f91825260209091200154909350600160301b90046001600160801b031691506127f89050565b5f815b808210156127c1575f60016127638385614538565b61276e906001614538565b901c90508765ffffffffffff1687828154811061278d5761278d61442c565b5f9182526020909120015465ffffffffffff16116127ad578092506127bb565b6127b860018261471c565b91505b5061274e565b818683815481106127d4576127d461442c565b5f91825260209091200154909550600160301b90046001600160801b031693505050505b9250929050565b6001600160a01b0382165f908152600d602052604090205460ff1615612823575050565b60085465ffffffffffff165f61283a8260016146fd565b6001600160a01b0385165f908152600a60205260408120805492935091908261286460018461471c565b815481106128745761287461442c565b5f918252602090912001805490915065ffffffffffff808716911611612a1357805465ffffffffffff8087169116036128f4578054869082906006906128cb908490600160301b90046001600160801b031661472f565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612982565b8260405180604001604052808765ffffffffffff16815260200188845f0160069054906101000a90046001600160801b0316612930919061472f565b6001600160801b0390811690915282546001810184555f9384526020938490208351910180549490930151909116600160301b026001600160b01b031990931665ffffffffffff909116179190911790555b65ffffffffffff85165f90815260096020526040812080548892906129b19084906001600160801b031661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508465ffffffffffff16876001600160a01b03165f8051602061479683398151915288604051611b1591906001600160801b0391909116815260200190565b81600103612aea57805486908290600690612a3f908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff86165f90815260096020526040812080548a94509092612a889185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508365ffffffffffff16876001600160a01b03165f8051602061479683398151915288604051611b1591906001600160801b0391909116815260200190565b5f83612af760028561471c565b81548110612b0757612b0761442c565b5f918252602082200180548454919350612b38916001600160801b03600160301b928390048116929091041661472f565b9050806001600160801b0316886001600160801b03161115612dfd575f612b5f828a61472f565b835490915065ffffffffffff808a16911603612c8457825481908490600690612b99908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff8a165f90815260096020526040812080548594509092612be29185911661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff89165f90815260096020526040812080548694509092612c2b9185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555085805480612c5f57612c5f61474f565b5f8281526020902081015f1990810180546001600160b01b0319169055019055612d75565b835465ffffffffffff191665ffffffffffff89161780855589908590600690612cbe908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff8a165f90815260096020526040812080548594509092612d079185911661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff89165f90815260096020526040812080548694509092612d509185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b6040516001600160801b038316815265ffffffffffff8816906001600160a01b038c16905f805160206147968339815191529060200160405180910390a36040516001600160801b038216815265ffffffffffff8916906001600160a01b038c16905f805160206147968339815191529060200160405180910390a350505050505050505050565b825488908490600690612e21908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff88165f90815260096020526040812080548c94509092612e6a9185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508565ffffffffffff16896001600160a01b03165f805160206147968339815191528a604051612ecc91906001600160801b0391909116815260200190565b60405180910390a3505050505050505050565b5f610e248383670de0b6b3a76400006130df565b6001600160a01b0382165f9081526005602052604081208054839290612f1a90849061471c565b90915550506004805482900390556040518181525f906001600160a01b038416905f805160206147b6833981519152906020015b60405180910390a35050565b606082612f6f57612f6a82613a6c565b610e24565b8151158015612f8657506001600160a01b0384163b155b15612faf57604051639996b31560e01b81526001600160a01b0385166004820152602401610bfc565b5080610e24565b6001600160a01b0383165f9081526006602090815260408083203384529091528120545f19811461300f57612feb838261471c565b6001600160a01b0386165f9081526006602090815260408083203384529091529020555b6001600160a01b0385165f908152600560205260408120805485929061303690849061471c565b90915550506001600160a01b038085165f81815260056020526040908190208054870190555190918716905f805160206147b68339815191529061307d9087815260200190565b60405180910390a3506001949350505050565b8060045f8282546130a19190614538565b90915550506001600160a01b0382165f818152600560209081526040808320805486019055518481525f805160206147b68339815191529101612f4e565b5f825f1904841183021582026130f3575f80fd5b5091020490565b5f8060608480830361311f576040516372c4448b60e11b815260040160405180910390fd5b806001600160401b0381111561313757613137613c4c565b60405190808252806020026020018201604052801561318757816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f199092019101816131555790505b5091505f6001600c5461319a919061471c565b905065ffffffffffff94505f93505f805f5b8481101561344a57838b8b838181106131c7576131c761442c565b9050602002013511156131ed576040516301dbc85f60e51b815260040160405180910390fd5b5f6101008c8c848181106132035761320361442c565b905060200201356132149190614583565b9050815f0361324b576001600160a01b038a165f908152600f60209081526040808320848452909152902054909350915082613284565b808414613284576001600160a01b038a165f908152600f6020908152604080832096835295905284812093909355808352929091205490825b5f6101008d8d8581811061329a5761329a61442c565b905060200201356132ab9190614763565b9050600184821c81161480156132f0578d8d858181106132cd576132cd61442c565b905060200201356040516340e47ccb60e11b8152600401610bfc91815260200190565b506001901b929092179150600b5f8c8c848181106133105761331061442c565b602090810292909201358352508181019290925260409081015f208151608081018352815465ffffffffffff8082168352600160301b82041694820194909452600160601b9093046001600160a01b03169183019190915260010154606082015286518790839081106133855761338561442c565b60200260200101819052508765ffffffffffff168682815181106133ab576133ab61442c565b60200260200101515f015165ffffffffffff1610156133e4578581815181106133d6576133d661442c565b60200260200101515f015197505b8665ffffffffffff168682815181106133ff576133ff61442c565b60200260200101516020015165ffffffffffff16111561343a5785818151811061342b5761342b61442c565b60200260200101516020015196505b61344381614496565b90506131ac565b506001600160a01b039097165f908152600f6020908152604080832093835292905220959095555091959094509092509050565b6060805f61348c8686614776565b6134979060016146fd565b65ffffffffffff169050806001600160401b038111156134b9576134b9613c4c565b6040519080825280602002602001820160405280156134e2578160200160208202803683370190505b509250806001600160401b038111156134fd576134fd613c4c565b604051908082528060200260200182016040528015613526578160200160208202803683370190505b5084549092505f806135388988612679565b9092506001600160801b031690505f61355260018561471c565b604080518082019091525f8082526020820152908410915081156135c8578861357c856001614538565b8154811061358c5761358c61442c565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160801b03169181019190915290505b8a5b8a65ffffffffffff168165ffffffffffff161161375357815165ffffffffffff82811691161480156135f95750825b156136865760208201516001600160801b0316935061361960018761471c565b61362286614496565b9550851092508215613686578961363a866001614538565b8154811061364a5761364a61442c565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160801b03169181019190915291505b65ffffffffffff81165f90815260096020526040902080546001600160801b03168015613740575f6136b88f85614776565b65ffffffffffff1690506136d5876001600160801b038416612292565b8c82815181106136e7576136e761442c565b60209081029190910101528254613717906001600160401b03600160801b8204811691600160c01b9004166144ae565b6001600160401b03168b82815181106137325761373261442c565b602002602001018181525050505b50508061374c9061454b565b90506135ca565b50505050505050935093915050565b60608086806001600160401b0381111561377e5761377e613c4c565b6040519080825280602002602001820160405280156137a7578160200160208202803683370190505b509250806001600160401b038111156137c2576137c2613c4c565b6040519080825280602002602001820160405280156137eb578160200160208202803683370190505b5091505f805b828110156139ec575f6138278a838151811061380f5761380f61442c565b60200260200101518a65ffffffffffff168a8a613a95565b905080156139db575f805b848110156138cc578b848151811061384c5761384c61442c565b6020026020010151604001516001600160a01b03168882815181106138735761387361442c565b60200260200101516001600160a01b0316036138bc578287828151811061389c5761389c61442c565b602002602001018181516138b09190614538565b905250600191506138cc565b6138c581614496565b9050613832565b508061394e578a83815181106138e4576138e461442c565b6020026020010151604001518785815181106139025761390261442c565b60200260200101906001600160a01b031690816001600160a01b031681525050818685815181106139355761393561442c565b60209081029190910101528361394a81614496565b9450505b8a83815181106139605761396061442c565b6020026020010151604001516001600160a01b0316336001600160a01b03167f82439202f33af72917462d0ce3d8c285b0d26c8c1d3e2797c600d8466dcbaff38f8f878181106139b2576139b261442c565b90506020020135856040516139d1929190918252602082015260400190565b60405180910390a3505b506139e581614496565b90506137f1565b50818110156139fc578084528083525b5050965096945050505050565b335f90815260056020526040812080548391908390613a2990849061471c565b90915550506001600160a01b0383165f81815260056020526040908190208054850190555133905f805160206147b683398151915290610b659086815260200190565b805115613a7c5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f808560600151670de0b6b3a7640000613aaf9190614521565b865190915065ffffffffffff165b866020015165ffffffffffff168111613b7557856001600160801b03168110613b65575f613af46001600160801b0388168361471c565b90505f868281518110613b0957613b0961442c565b602002602001015190505f811115613b62575f613b48878481518110613b3157613b3161442c565b602002602001015186612edf90919063ffffffff16565b9050613b548183612edf565b613b5e9087614538565b9550505b50505b613b6e81614496565b9050613abd565b50612099670de0b6b3a764000083614583565b5f60208284031215613b98575f80fd5b81356001600160e01b031981168114610e24575f80fd5b5f5b83811015613bc9578181015183820152602001613bb1565b50505f910152565b5f8151808452613be8816020860160208601613baf565b601f01601f19169290920160200192915050565b602081525f610e246020830184613bd1565b6001600160a01b03811681146115f2575f80fd5b5f8060408385031215613c33575f80fd5b8235613c3e81613c0e565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c8857613c88613c4c565b604052919050565b5f82601f830112613c9f575f80fd5b81356001600160401b03811115613cb857613cb8613c4c565b613ccb601f8201601f1916602001613c60565b818152846020838601011115613cdf575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f8060808587031215613d0e575f80fd5b8435613d1981613c0e565b93506020850135613d2981613c0e565b92506040850135915060608501356001600160401b03811115613d4a575f80fd5b613d5687828801613c90565b91505092959194509250565b5f805f805f60a08688031215613d76575f80fd5b8535613d8181613c0e565b94506020860135613d9181613c0e565b9350604086013592506060860135613da881613c0e565b949793965091946080013592915050565b5f8083601f840112613dc9575f80fd5b5081356001600160401b03811115613ddf575f80fd5b6020830191508360208260051b85010111156127f8575f80fd5b5f805f805f8060608789031215613e0e575f80fd5b86356001600160401b0380821115613e24575f80fd5b613e308a838b01613db9565b90985096506020890135915080821115613e48575f80fd5b613e548a838b01613db9565b90965094506040890135915080821115613e6c575f80fd5b50613e7989828a01613db9565b979a9699509497509295939492505050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015613ede57603f19888603018452613ecc858351613bd1565b94509285019290850190600101613eb0565b5092979650505050505050565b5f805f60608486031215613efd575f80fd5b8335613f0881613c0e565b92506020840135613f1881613c0e565b929592945050506040919091013590565b5f60208284031215613f39575f80fd5b8135610e2481613c0e565b5f805f805f805f806080898b031215613f5b575f80fd5b88356001600160401b0380821115613f71575f80fd5b613f7d8c838d01613db9565b909a50985060208b0135915080821115613f95575f80fd5b613fa18c838d01613db9565b909850965060408b0135915080821115613fb9575f80fd5b613fc58c838d01613db9565b909650945060608b0135915080821115613fdd575f80fd5b50613fea8b828c01613db9565b999c989b5096995094979396929594505050565b602080825282518282018190525f919060409081850190868401855b82811015614050578151805165ffffffffffff1685528601516001600160801b031686850152928401929085019060010161401a565b5091979650505050505050565b5f806040838503121561406e575f80fd5b823561407981613c0e565b9150602083013561408981613c0e565b809150509250929050565b5f80602083850312156140a5575f80fd5b82356001600160401b038111156140ba575f80fd5b6140c685828601613db9565b90969095509350505050565b5f602082840312156140e2575f80fd5b813565ffffffffffff81168114610e24575f80fd5b5f805f60408486031215614109575f80fd5b83356001600160401b0381111561411e575f80fd5b61412a86828701613db9565b909450925050602084013561413e81613c0e565b809150509250925092565b5f82601f830112614158575f80fd5b813560206001600160401b0382111561417357614173613c4c565b8160051b614182828201613c60565b928352848101820192828101908785111561419b575f80fd5b83870192505b84831015611cda578235825291830191908301906141a1565b5f805f805f60a086880312156141ce575f80fd5b85356141d981613c0e565b945060208601356141e981613c0e565b935060408601356001600160401b0380821115614204575f80fd5b61421089838a01614149565b94506060880135915080821115614225575f80fd5b61423189838a01614149565b93506080880135915080821115614246575f80fd5b5061425388828901613c90565b9150509295509295909350565b5f805f805f805f60e0888a031215614276575f80fd5b873561428181613c0e565b9650602088013561429181613c0e565b95506040880135945060608801359350608088013560ff811681146142b4575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f805f805f60a086880312156142e5575f80fd5b85356142f081613c0e565b9450602086013561430081613c0e565b9350604086013592506060860135915060808601356001600160401b03811115614328575f80fd5b61425388828901613c90565b5f60208284031215614344575f80fd5b5035919050565b5f805f806060858703121561435e575f80fd5b843561436981613c0e565b935060208501356001600160401b0380821115614384575f80fd5b818701915087601f830112614397575f80fd5b8135818111156143a5575f80fd5b8860208285010111156143b6575f80fd5b95986020929092019750949560400135945092505050565b600181811c908216806143e257607f821691505b60208210810361440057634e487b7160e01b5f52602260045260245ffd5b50919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614455575f80fd5b8301803591506001600160401b0382111561446e575f80fd5b6020019150368190038213156127f8575f80fd5b634e487b7160e01b5f52601160045260245ffd5b5f600182016144a7576144a7614482565b5060010190565b6001600160401b038281168282160390808211156144ce576144ce614482565b5092915050565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b5f60208284031215614512575f80fd5b81518015158114610e24575f80fd5b8082028115828204841417610a7957610a79614482565b80820180821115610a7957610a79614482565b5f65ffffffffffff80831681810361456557614565614482565b6001019392505050565b634e487b7160e01b5f52601260045260245ffd5b5f826145915761459161456f565b500490565b6001600160801b038181168382160190808211156144ce576144ce614482565b5f82516145c7818460208701613baf565b9190910192915050565b5f80835481600182811c9150808316806145ec57607f831692505b6020808410820361460b57634e487b7160e01b86526022600452602486fd5b81801561461f57600181146146345761465f565b60ff198616895284151585028901965061465f565b5f8a8152602090205f5b868110156146575781548b82015290850190830161463e565b505084890196505b509498975050505050505050565b606080825284519082018190525f906020906080840190828801845b828110156146ae5781516001600160a01b031684529284019290840190600101614689565b505050838103828501528551808252868301918301905f5b818110156146e2578351835292840192918401916001016146c6565b50506001600160a01b03861660408601529250610bc3915050565b65ffffffffffff8181168382160190808211156144ce576144ce614482565b81810381811115610a7957610a79614482565b6001600160801b038281168282160390808211156144ce576144ce614482565b634e487b7160e01b5f52603160045260245ffd5b5f826147715761477161456f565b500690565b65ffffffffffff8281168282160390808211156144ce576144ce61448256fe632056710afd218e9699548c7020044b84740417c6b482b438054907f72cceaeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220f7f293b82cf6c6f06c88df7d8f082e34f045ec4d595593166f52c065695ef13c64736f6c63430008150033608060405234801561000f575f80fd5b505f80546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506104dd8061005d5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806375992d3c146100435780638da5cb5b14610058578063f2fde38b14610086575b5f80fd5b610056610051366004610386565b610099565b005b5f5461006a906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b61005661009436600461044f565b61017a565b5f546001600160a01b031633146100e65760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b60448201526064015b60405180910390fd5b8251825181146101095760405163512509d360e11b815260040160405180910390fd5b5f5b8181101561017357610163838583815181106101295761012961046f565b60200260200101518784815181106101435761014361046f565b60200260200101516001600160a01b031661020c9092919063ffffffff16565b61016c81610483565b905061010b565b5050505050565b5f546001600160a01b031633146101c25760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b60448201526064016100dd565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156102565750823b153d17155b806102955760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016100dd565b50505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156102d8576102d861029b565b604052919050565b5f67ffffffffffffffff8211156102f9576102f961029b565b5060051b60200190565b80356001600160a01b0381168114610319575f80fd5b919050565b5f82601f83011261032d575f80fd5b8135602061034261033d836102e0565b6102af565b82815260059290921b84018101918181019086841115610360575f80fd5b8286015b8481101561037b5780358352918301918301610364565b509695505050505050565b5f805f60608486031215610398575f80fd5b833567ffffffffffffffff808211156103af575f80fd5b818601915086601f8301126103c2575f80fd5b813560206103d261033d836102e0565b82815260059290921b8401810191818101908a8411156103f0575f80fd5b948201945b838610156104155761040686610303565b825294820194908201906103f5565b9750508701359250508082111561042a575f80fd5b506104378682870161031e565b92505061044660408501610303565b90509250925092565b5f6020828403121561045f575f80fd5b61046882610303565b9392505050565b634e487b7160e01b5f52603260045260245ffd5b5f600182016104a057634e487b7160e01b5f52601160045260245ffd5b506001019056fea2646970667358221220448b9387a0e251403453025084a9a5810d9e05a9ba53dbb3c6a19102633462fc64736f6c634300081500330000000000000000000000005f2f11ad8656439d5c14d9b351f8b09cdac2a02d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010526f79636f2055534443205661756c74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007726f795553444300000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405260043610610278575f3560e01c80637a9e5e4b1161014a578063b7ba9fa2116100be578063dd62ed3e11610078578063dd62ed3e146108f9578063f23a6e611461092f578063f2fde38b1461095a578063f301af4214610979578063f549794014610a0b578063f6e715d014610a2a575f80fd5b8063b7ba9fa21461081b578063b82148141461083a578063bc197c8114610871578063bf7e214f1461089c578063c7ad155a146108bb578063d505accf146108da575f80fd5b80638da5cb5b1161010f5780638da5cb5b1461074d5780639579b6891461076b57806395d89b411461079e5780639c0fed1f146107b2578063a887b71c146107e8578063a9059cbb146107fc575f80fd5b80637a9e5e4b146106915780637d2066e6146106b05780637ecebe00146106e45780637f5a7c7b1461070f5780638929565f1461072e575f80fd5b80633644e515116101ec5780635c2ff63b116101a65780635c2ff63b1461055f5780635eac62391461057457806366c6822c1461059357806370a0823114610612578063766718081461063d578063773caf4114610672575f80fd5b80633644e51514610476578063369874101461048a57806339d6ba32146104d657806354ee9b52146104f5578063556c6f68146105145780635a795d6614610540575f80fd5b806318160ddd1161023d57806318160ddd1461037457806318457e6114610397578063224d8703146103b857806323b872dd146103e45780632fd867b114610403578063313ce56714610431575f80fd5b806301ffc9a71461028357806306fdde03146102b7578063095ea7b3146102d85780630e9de728146102f7578063150b7a021461033c575f80fd5b3661027f57005b5f80fd5b34801561028e575f80fd5b506102a261029d366004613b88565b610a49565b60405190151581526020015b60405180910390f35b3480156102c2575f80fd5b506102cb610a7f565b6040516102ae9190613bfc565b3480156102e3575f80fd5b506102a26102f2366004613c22565b610b0b565b348015610302575f80fd5b50610316610311366004613c22565b610b76565b6040805165ffffffffffff90931683526001600160801b039091166020830152016102ae565b348015610347575f80fd5b5061035b610356366004613cfb565b610bba565b6040516001600160e01b031990911681526020016102ae565b34801561037f575f80fd5b5061038960045481565b6040519081526020016102ae565b3480156103a2575f80fd5b506103b66103b1366004613d62565b610bcb565b005b3480156103c3575f80fd5b506103d76103d2366004613df9565b610c90565b6040516102ae9190613e8b565b3480156103ef575f80fd5b506102a26103fe366004613eeb565b610e0b565b34801561040e575f80fd5b506102a261041d366004613f29565b600d6020525f908152604090205460ff1681565b34801561043c575f80fd5b506104647f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff90911681526020016102ae565b348015610481575f80fd5b50610389610e2b565b348015610495575f80fd5b506104be6104a4366004613f29565b600e6020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102ae565b3480156104e1575f80fd5b506103b66104f0366004613d62565b610e85565b348015610500575f80fd5b506103b661050f366004613f44565b610f33565b34801561051f575f80fd5b5061053361052e366004613f29565b611423565b6040516102ae9190613ffe565b34801561054b575f80fd5b506103b661055a36600461405d565b6114b2565b34801561056a575f80fd5b50610389600c5481565b34801561057f575f80fd5b506103b661058e366004614094565b611510565b34801561059e575f80fd5b506105e36105ad3660046140d2565b60096020525f90815260409020546001600160801b038116906001600160401b03600160801b8204811691600160c01b90041683565b604080516001600160801b0390941684526001600160401b0392831660208501529116908201526060016102ae565b34801561061d575f80fd5b5061038961062c366004613f29565b60056020525f908152604090205481565b348015610648575f80fd5b5060085461065b9065ffffffffffff1681565b60405165ffffffffffff90911681526020016102ae565b34801561067d575f80fd5b506103b661068c366004613f29565b611550565b34801561069c575f80fd5b506103b66106ab366004613f29565b6115f5565b3480156106bb575f80fd5b506103896106ca366004613f29565b6001600160a01b03165f908152600a602052604090205490565b3480156106ef575f80fd5b506103896106fe366004613f29565b60076020525f908152604090205481565b34801561071a575f80fd5b506010546104be906001600160a01b031681565b348015610739575f80fd5b506103b6610748366004613f29565b6116d9565b348015610758575f80fd5b505f546104be906001600160a01b031681565b348015610776575f80fd5b506104be7f00000000000000000000000037569debe92321c7a44afeb299e5b0dbc97d386681565b3480156107a9575f80fd5b506102cb61172c565b3480156107bd575f80fd5b506103896107cc366004613c22565b600f60209081525f928352604080842090915290825290205481565b3480156107f3575f80fd5b506103b6611739565b348015610807575f80fd5b506102a2610816366004613c22565b611774565b348015610826575f80fd5b506103b66108353660046140f7565b611789565b348015610845575f80fd5b50610859610854366004613f29565b611803565b6040516001600160801b0390911681526020016102ae565b34801561087c575f80fd5b5061035b61088b3660046141ba565b63bc197c8160e01b95945050505050565b3480156108a7575f80fd5b506001546104be906001600160a01b031681565b3480156108c6575f80fd5b506103b66108d5366004613f29565b611833565b3480156108e5575f80fd5b506103b66108f4366004614260565b6118e7565b348015610904575f80fd5b5061038961091336600461405d565b600660209081525f928352604080842090915290825290205481565b34801561093a575f80fd5b5061035b6109493660046142d1565b63f23a6e6160e01b95945050505050565b348015610965575f80fd5b506103b6610974366004613f29565b611b26565b348015610984575f80fd5b506109d3610993366004614334565b600b6020525f90815260409020805460019091015465ffffffffffff80831692600160301b810490911691600160601b9091046001600160a01b03169084565b6040805165ffffffffffff95861681529490931660208501526001600160a01b039091169183019190915260608201526080016102ae565b348015610a16575f80fd5b50610389610a25366004613c22565b611ba1565b348015610a35575f80fd5b506102cb610a4436600461434b565b611ce5565b5f6001600160e01b03198216630271189760e51b1480610a7957506301ffc9a760e01b6001600160e01b03198316145b92915050565b60028054610a8c906143ce565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab8906143ce565b8015610b035780601f10610ada57610100808354040283529160200191610b03565b820191905f5260205f20905b815481529060010190602001808311610ae657829003601f168201915b505050505081565b335f8181526006602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610b659086815260200190565b60405180910390a350600192915050565b600a602052815f5260405f208181548110610b8f575f80fd5b5f9182526020909120015465ffffffffffff81169250600160301b90046001600160801b0316905082565b630a85bd0160e11b5b949350505050565b610be0335f356001600160e01b031916611d73565b610c055760405162461bcd60e51b8152600401610bfc90614406565b60405180910390fd5b610c0f8282611e18565b8215610c2957610c296001600160a01b0385168685611e2c565b816001600160a01b0316846001600160a01b0316866001600160a01b03167fe0c82280a1164680e0cf43be7db4c4c9f985423623ad7a544fb76c772bdc60438685604051610c81929190918252602082015260400190565b60405180910390a45050505050565b6060610ca7335f356001600160e01b031916611d73565b610cc35760405162461bcd60e51b8152600401610bfc90614406565b610ccb611ebb565b85806001600160401b03811115610ce457610ce4613c4c565b604051908082528060200260200182016040528015610d1757816020015b6060815260200190600190039081610d025790505b5091505f5b81811015610dff57610dd1878783818110610d3957610d3961442c565b9050602002810190610d4b9190614440565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150859050818110610d9357610d9361442c565b905060200201358b8b85818110610dac57610dac61442c565b9050602002016020810190610dc19190613f29565b6001600160a01b03169190612003565b838281518110610de357610de361442c565b602002602001018190525080610df890614496565b9050610d1c565b50509695505050505050565b5f610e1684846120a3565b610e2184848461211f565b90505b9392505050565b5f7f00000000000000000000000000000000000000000000000000000000000000924614610e6057610e5b612141565b905090565b507f2309a4adc61c3c11319041dc4895f2797befd7bba81b746bde13aaa918501d1d90565b610e9a335f356001600160e01b031916611d73565b610eb65760405162461bcd60e51b8152600401610bfc90614406565b8215610ed157610ed16001600160a01b0385168630866121da565b610edb828261227e565b816001600160a01b0316846001600160a01b0316866001600160a01b03167fea00f88768a86184a6e515238a549c171769fe7460a011d6fd0bcd48ca078ea48685604051610c81929190918252602082015260400190565b610f48335f356001600160e01b031916611d73565b610f645760405162461bcd60e51b8152600401610bfc90614406565b868581141580610f745750808414155b80610f7f5750808214155b15610f9d5760405163512509d360e11b815260040160405180910390fd5b5f5b8181101561141757838382818110610fb957610fb961442c565b9050602002016020810190610fce91906140d2565b65ffffffffffff16868683818110610fe857610fe861442c565b9050602002016020810190610ffd91906140d2565b65ffffffffffff16111561102457604051630c2b941b60e41b815260040160405180910390fd5b60085465ffffffffffff168484838181106110415761104161442c565b905060200201602081019061105691906140d2565b65ffffffffffff161061107c5760405163383ed2a960e01b815260040160405180910390fd5b5f60095f8888858181106110925761109261442c565b90506020020160208101906110a791906140d2565b65ffffffffffff1665ffffffffffff1681526020019081526020015f2090505f60095f8787868181106110dc576110dc61442c565b90506020020160208101906110f191906140d2565b65ffffffffffff1665ffffffffffff1681526020019081526020015f209050604051806080016040528089898681811061112d5761112d61442c565b905060200201602081019061114291906140d2565b65ffffffffffff1681526020018787868181106111615761116161442c565b905060200201602081019061117691906140d2565b65ffffffffffff1681526020018d8d868181106111955761119561442c565b90506020020160208101906111aa9190613f29565b6001600160a01b0316815283548354602090920191611218916111e8916001600160401b03600160801b909204821691600160c01b909104166144ae565b6001600160401b03168d8d888181106112035761120361442c565b9050602002013561229290919063ffffffff16565b9052600c545f908152600b602090815260409182902083518154928501519385015165ffffffffffff9182166bffffffffffffffffffffffff1990941693909317600160301b9190941602929092176bffffffffffffffffffffffff16600160601b6001600160a01b039092169190910217815560609091015160019091015561130e337f00000000000000000000000037569debe92321c7a44afeb299e5b0dbc97d38668c8c878181106112cf576112cf61442c565b905060200201358f8f888181106112e8576112e861442c565b90506020020160208101906112fd9190613f29565b6001600160a01b03169291906121da565b8585848181106113205761132061442c565b905060200201602081019061133591906140d2565b65ffffffffffff1688888581811061134f5761134f61442c565b905060200201602081019061136491906140d2565b65ffffffffffff168d8d8681811061137e5761137e61442c565b90506020020160208101906113939190613f29565b6001600160a01b03167fdb2ae7fd4c873ab5f72ed0c2a86f8403da7fca4a50110ef5381f6c365829322d8d8d888181106113cf576113cf61442c565b90506020020135600c5f8154809291906113e890614496565b909155506040805192835260208301919091520160405180910390a450508061141090614496565b9050610f9f565b50505050505050505050565b6001600160a01b0381165f908152600a60209081526040808320805482518185028101850190935280835260609492939192909184015b828210156114a7575f848152602090819020604080518082019091529084015465ffffffffffff81168252600160301b90046001600160801b03168183015282526001909201910161145a565b505050509050919050565b6114c7335f356001600160e01b031916611d73565b6114e35760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b039182165f908152600e6020526040902080546001600160a01b03191691909216179055565b611525335f356001600160e01b031916611d73565b6115415760405162461bcd60e51b8152600401610bfc90614406565b61154c8282336122a6565b5050565b611565335f356001600160e01b031916611d73565b6115815760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b0381165f908152600d602052604081205460ff16151590036115bd5760405163e58d4f7960e01b815260040160405180910390fd5b6001600160a01b0381165f908152600d60209081526040808320805460ff1916905560059091529020546115f29082906123bd565b50565b5f546001600160a01b0316331480611686575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061164790339030906001600160e01b03195f3516906004016144d5565b602060405180830381865afa158015611662573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116869190614502565b61168e575f80fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198905f90a350565b6116ee335f356001600160e01b031916611d73565b61170a5760405162461bcd60e51b8152600401610bfc90614406565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b60038054610a8c906143ce565b61174e335f356001600160e01b031916611d73565b61176a5760405162461bcd60e51b8152600401610bfc90614406565b611772611ebb565b565b5f61177f33846120a3565b610e248383612658565b61179e335f356001600160e01b031916611d73565b6117ba5760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b038181165f908152600e60205260409020541633146117f357604051632070af6360e01b815260040160405180910390fd5b6117fe8383836122a6565b505050565b6008546001600160a01b0382165f908152600a6020526040812090918291610bc39165ffffffffffff1690612679565b611848335f356001600160e01b031916611d73565b6118645760405162461bcd60e51b8152600401610bfc90614406565b6001600160a01b0381165f908152600d602052604090205460ff1615156001036118a157604051632ebc8f1160e11b815260040160405180910390fd5b6001600160a01b0381165f908152600560205260409020546118c49082906127ff565b6001600160a01b03165f908152600d60205260409020805460ff19166001179055565b428410156119375760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610bfc565b5f6001611942610e2b565b6001600160a01b038a81165f8181526007602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611a4a573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811615801590611a805750876001600160a01b0316816001600160a01b0316145b611abd5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610bfc565b6001600160a01b039081165f9081526006602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a350505050505050565b611b3b335f356001600160e01b031916611d73565b611b575760405162461bcd60e51b8152600401610bfc90614406565b5f80546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b6001600160a01b0382165f908152600a60209081526040808320848452600b9092528220600181015483908190611be090670de0b6b3a7640000614521565b835490915065ffffffffffff165b835465ffffffffffff600160301b909104811690821611611cc7575f611c148287612679565b9150506001600160801b03811615611cb45765ffffffffffff82165f9081526009602052604081208054909190611c57906001600160801b038581169116612292565b82549091505f90611c81906001600160401b03600160801b8204811691600160c01b9004166144ae565b6001600160401b031690505f611c978783612edf565b9050611ca38184612edf565b611cad9089614538565b9750505050505b5080611cbf8161454b565b915050611bee565b50611cda670de0b6b3a764000083614583565b979650505050505050565b6060611cfc335f356001600160e01b031916611d73565b611d185760405162461bcd60e51b8152600401610bfc90614406565b611d20611ebb565b611d6a84848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050506001600160a01b03881691905084612003565b95945050505050565b6001545f906001600160a01b03168015801590611dfa575060405163b700961360e01b81526001600160a01b0382169063b700961390611dbb908790309088906004016144d5565b602060405180830381865afa158015611dd6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dfa9190614502565b80610bc357505f546001600160a01b03858116911614949350505050565b611e228282612ef3565b61154c82826127ff565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f511416151615611e765750823b153d17155b80611eb55760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610bfc565b50505050565b600880545f9165ffffffffffff9091169082611ed68361454b565b82546101009290920a65ffffffffffff81810219909316918316021790915581165f908152600960208190526040822092935081611f138561454b565b65ffffffffffff8116825260208201929092526040015f90812084546001600160401b034216600160c01b81026001600160c01b039092169190911786558154600160801b90910267ffffffffffffffff60801b1982168117835586549497509194506001600160801b0393841693859392611f96928692918216911617614596565b82546101009290920a6001600160801b0381810219909316918316021790915582546040805191909216815242602082015265ffffffffffff861692507f29db3deb62ef2036e5eb93aad68d2362aec0711af592cb365566603bd88651d4910160405180910390a2505050565b60608147101561202f5760405163cf47918160e01b815247600482015260248101839052604401610bfc565b5f80856001600160a01b0316848660405161204a91906145b6565b5f6040518083038185875af1925050503d805f8114612084576040519150601f19603f3d011682016040523d82523d5f602084013e612089565b606091505b5091509150612099868383612f5a565b9695505050505050565b6010546001600160a01b03161561154c57601054604051630abd626b60e41b81526001600160a01b03848116600483015283811660248301523360448301529091169063abd626b0906064015f6040518083038186803b158015612105575f80fd5b505afa158015612117573d5f803e3d5ffd5b505050505050565b5f61212b848484612fb6565b905061213784836127ff565b610e2483836123bd565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600260405161217291906145d1565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af191505080601f3d1160015f5114161516156122335750833b153d17155b806122775760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bfc565b5050505050565b6122888282613090565b61154c82826123bd565b5f610e2483670de0b6b3a7640000846130df565b5f805f6122b48686866130fa565b6001600160a01b0387165f908152600a6020526040812080549497509295509093509091829082906122e8576122e861442c565b5f9182526020909120015465ffffffffffff9081169150851681111561230c578094505b5f8061231987878661347e565b915091505f8061232d8c8c898c8888613762565b604051631d664b4f60e21b815291935091506001600160a01b037f00000000000000000000000037569debe92321c7a44afeb299e5b0dbc97d386616906375992d3c906123829085908590339060040161466d565b5f604051808303815f87803b158015612399575f80fd5b505af11580156123ab573d5f803e3d5ffd5b50505050505050505050505050505050565b6001600160a01b0382165f908152600d602052604090205460ff16156123e1575050565b6008545f906123f99065ffffffffffff1660016146fd565b6001600160a01b0384165f908152600a602052604081208054929350919003612482576040805180820190915265ffffffffffff80841682526001600160801b03808616602080850191825285546001810187555f878152919091209451940180549151909216600160301b026001600160b01b031990911693909216929092171790556125a1565b80545f9082906124949060019061471c565b815481106124a4576124a461442c565b5f918252602090912001805490915065ffffffffffff808516911603612511578054849082906006906124e8908490600160301b90046001600160801b0316614596565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061259f565b8160405180604001604052808565ffffffffffff16815260200186845f0160069054906101000a90046001600160801b031661254d9190614596565b6001600160801b0390811690915282546001810184555f9384526020938490208351910180549490930151909116600160301b026001600160b01b031990931665ffffffffffff909116179190911790555b505b65ffffffffffff82165f9081526009602052604081208054909185918391906125d49084906001600160801b0316614596565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508265ffffffffffff16856001600160a01b03167ff618eaac43bda1b7a8ca1344c0d6084f93d0b08e7beb0554b03ba72c4ca72bac8660405161264991906001600160801b0391909116815260200190565b60405180910390a35050505050565b5f6126638383613a09565b905061266f33836127ff565b610a7983836123bd565b80545f908190810361268f57505f9050806127f8565b825f815481106126a1576126a161442c565b5f9182526020909120015465ffffffffffff90811690851610156126d057505065ffffffffffff82165f6127f8565b82545f906126e09060019061471c565b90508381815481106126f4576126f461442c565b5f9182526020909120015465ffffffffffff9081169086161061274b57808482815481106127245761272461442c565b5f91825260209091200154909350600160301b90046001600160801b031691506127f89050565b5f815b808210156127c1575f60016127638385614538565b61276e906001614538565b901c90508765ffffffffffff1687828154811061278d5761278d61442c565b5f9182526020909120015465ffffffffffff16116127ad578092506127bb565b6127b860018261471c565b91505b5061274e565b818683815481106127d4576127d461442c565b5f91825260209091200154909550600160301b90046001600160801b031693505050505b9250929050565b6001600160a01b0382165f908152600d602052604090205460ff1615612823575050565b60085465ffffffffffff165f61283a8260016146fd565b6001600160a01b0385165f908152600a60205260408120805492935091908261286460018461471c565b815481106128745761287461442c565b5f918252602090912001805490915065ffffffffffff808716911611612a1357805465ffffffffffff8087169116036128f4578054869082906006906128cb908490600160301b90046001600160801b031661472f565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612982565b8260405180604001604052808765ffffffffffff16815260200188845f0160069054906101000a90046001600160801b0316612930919061472f565b6001600160801b0390811690915282546001810184555f9384526020938490208351910180549490930151909116600160301b026001600160b01b031990931665ffffffffffff909116179190911790555b65ffffffffffff85165f90815260096020526040812080548892906129b19084906001600160801b031661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508465ffffffffffff16876001600160a01b03165f8051602061479683398151915288604051611b1591906001600160801b0391909116815260200190565b81600103612aea57805486908290600690612a3f908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff86165f90815260096020526040812080548a94509092612a889185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508365ffffffffffff16876001600160a01b03165f8051602061479683398151915288604051611b1591906001600160801b0391909116815260200190565b5f83612af760028561471c565b81548110612b0757612b0761442c565b5f918252602082200180548454919350612b38916001600160801b03600160301b928390048116929091041661472f565b9050806001600160801b0316886001600160801b03161115612dfd575f612b5f828a61472f565b835490915065ffffffffffff808a16911603612c8457825481908490600690612b99908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff8a165f90815260096020526040812080548594509092612be29185911661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff89165f90815260096020526040812080548694509092612c2b9185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555085805480612c5f57612c5f61474f565b5f8281526020902081015f1990810180546001600160b01b0319169055019055612d75565b835465ffffffffffff191665ffffffffffff89161780855589908590600690612cbe908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff8a165f90815260096020526040812080548594509092612d079185911661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff89165f90815260096020526040812080548694509092612d509185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b6040516001600160801b038316815265ffffffffffff8816906001600160a01b038c16905f805160206147968339815191529060200160405180910390a36040516001600160801b038216815265ffffffffffff8916906001600160a01b038c16905f805160206147968339815191529060200160405180910390a350505050505050505050565b825488908490600690612e21908490600160301b90046001600160801b031661472f565b82546101009290920a6001600160801b0381810219909316918316021790915565ffffffffffff88165f90815260096020526040812080548c94509092612e6a9185911661472f565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508565ffffffffffff16896001600160a01b03165f805160206147968339815191528a604051612ecc91906001600160801b0391909116815260200190565b60405180910390a3505050505050505050565b5f610e248383670de0b6b3a76400006130df565b6001600160a01b0382165f9081526005602052604081208054839290612f1a90849061471c565b90915550506004805482900390556040518181525f906001600160a01b038416905f805160206147b6833981519152906020015b60405180910390a35050565b606082612f6f57612f6a82613a6c565b610e24565b8151158015612f8657506001600160a01b0384163b155b15612faf57604051639996b31560e01b81526001600160a01b0385166004820152602401610bfc565b5080610e24565b6001600160a01b0383165f9081526006602090815260408083203384529091528120545f19811461300f57612feb838261471c565b6001600160a01b0386165f9081526006602090815260408083203384529091529020555b6001600160a01b0385165f908152600560205260408120805485929061303690849061471c565b90915550506001600160a01b038085165f81815260056020526040908190208054870190555190918716905f805160206147b68339815191529061307d9087815260200190565b60405180910390a3506001949350505050565b8060045f8282546130a19190614538565b90915550506001600160a01b0382165f818152600560209081526040808320805486019055518481525f805160206147b68339815191529101612f4e565b5f825f1904841183021582026130f3575f80fd5b5091020490565b5f8060608480830361311f576040516372c4448b60e11b815260040160405180910390fd5b806001600160401b0381111561313757613137613c4c565b60405190808252806020026020018201604052801561318757816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f199092019101816131555790505b5091505f6001600c5461319a919061471c565b905065ffffffffffff94505f93505f805f5b8481101561344a57838b8b838181106131c7576131c761442c565b9050602002013511156131ed576040516301dbc85f60e51b815260040160405180910390fd5b5f6101008c8c848181106132035761320361442c565b905060200201356132149190614583565b9050815f0361324b576001600160a01b038a165f908152600f60209081526040808320848452909152902054909350915082613284565b808414613284576001600160a01b038a165f908152600f6020908152604080832096835295905284812093909355808352929091205490825b5f6101008d8d8581811061329a5761329a61442c565b905060200201356132ab9190614763565b9050600184821c81161480156132f0578d8d858181106132cd576132cd61442c565b905060200201356040516340e47ccb60e11b8152600401610bfc91815260200190565b506001901b929092179150600b5f8c8c848181106133105761331061442c565b602090810292909201358352508181019290925260409081015f208151608081018352815465ffffffffffff8082168352600160301b82041694820194909452600160601b9093046001600160a01b03169183019190915260010154606082015286518790839081106133855761338561442c565b60200260200101819052508765ffffffffffff168682815181106133ab576133ab61442c565b60200260200101515f015165ffffffffffff1610156133e4578581815181106133d6576133d661442c565b60200260200101515f015197505b8665ffffffffffff168682815181106133ff576133ff61442c565b60200260200101516020015165ffffffffffff16111561343a5785818151811061342b5761342b61442c565b60200260200101516020015196505b61344381614496565b90506131ac565b506001600160a01b039097165f908152600f6020908152604080832093835292905220959095555091959094509092509050565b6060805f61348c8686614776565b6134979060016146fd565b65ffffffffffff169050806001600160401b038111156134b9576134b9613c4c565b6040519080825280602002602001820160405280156134e2578160200160208202803683370190505b509250806001600160401b038111156134fd576134fd613c4c565b604051908082528060200260200182016040528015613526578160200160208202803683370190505b5084549092505f806135388988612679565b9092506001600160801b031690505f61355260018561471c565b604080518082019091525f8082526020820152908410915081156135c8578861357c856001614538565b8154811061358c5761358c61442c565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160801b03169181019190915290505b8a5b8a65ffffffffffff168165ffffffffffff161161375357815165ffffffffffff82811691161480156135f95750825b156136865760208201516001600160801b0316935061361960018761471c565b61362286614496565b9550851092508215613686578961363a866001614538565b8154811061364a5761364a61442c565b5f9182526020918290206040805180820190915291015465ffffffffffff81168252600160301b90046001600160801b03169181019190915291505b65ffffffffffff81165f90815260096020526040902080546001600160801b03168015613740575f6136b88f85614776565b65ffffffffffff1690506136d5876001600160801b038416612292565b8c82815181106136e7576136e761442c565b60209081029190910101528254613717906001600160401b03600160801b8204811691600160c01b9004166144ae565b6001600160401b03168b82815181106137325761373261442c565b602002602001018181525050505b50508061374c9061454b565b90506135ca565b50505050505050935093915050565b60608086806001600160401b0381111561377e5761377e613c4c565b6040519080825280602002602001820160405280156137a7578160200160208202803683370190505b509250806001600160401b038111156137c2576137c2613c4c565b6040519080825280602002602001820160405280156137eb578160200160208202803683370190505b5091505f805b828110156139ec575f6138278a838151811061380f5761380f61442c565b60200260200101518a65ffffffffffff168a8a613a95565b905080156139db575f805b848110156138cc578b848151811061384c5761384c61442c565b6020026020010151604001516001600160a01b03168882815181106138735761387361442c565b60200260200101516001600160a01b0316036138bc578287828151811061389c5761389c61442c565b602002602001018181516138b09190614538565b905250600191506138cc565b6138c581614496565b9050613832565b508061394e578a83815181106138e4576138e461442c565b6020026020010151604001518785815181106139025761390261442c565b60200260200101906001600160a01b031690816001600160a01b031681525050818685815181106139355761393561442c565b60209081029190910101528361394a81614496565b9450505b8a83815181106139605761396061442c565b6020026020010151604001516001600160a01b0316336001600160a01b03167f82439202f33af72917462d0ce3d8c285b0d26c8c1d3e2797c600d8466dcbaff38f8f878181106139b2576139b261442c565b90506020020135856040516139d1929190918252602082015260400190565b60405180910390a3505b506139e581614496565b90506137f1565b50818110156139fc578084528083525b5050965096945050505050565b335f90815260056020526040812080548391908390613a2990849061471c565b90915550506001600160a01b0383165f81815260056020526040908190208054850190555133905f805160206147b683398151915290610b659086815260200190565b805115613a7c5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f808560600151670de0b6b3a7640000613aaf9190614521565b865190915065ffffffffffff165b866020015165ffffffffffff168111613b7557856001600160801b03168110613b65575f613af46001600160801b0388168361471c565b90505f868281518110613b0957613b0961442c565b602002602001015190505f811115613b62575f613b48878481518110613b3157613b3161442c565b602002602001015186612edf90919063ffffffff16565b9050613b548183612edf565b613b5e9087614538565b9550505b50505b613b6e81614496565b9050613abd565b50612099670de0b6b3a764000083614583565b5f60208284031215613b98575f80fd5b81356001600160e01b031981168114610e24575f80fd5b5f5b83811015613bc9578181015183820152602001613bb1565b50505f910152565b5f8151808452613be8816020860160208601613baf565b601f01601f19169290920160200192915050565b602081525f610e246020830184613bd1565b6001600160a01b03811681146115f2575f80fd5b5f8060408385031215613c33575f80fd5b8235613c3e81613c0e565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c8857613c88613c4c565b604052919050565b5f82601f830112613c9f575f80fd5b81356001600160401b03811115613cb857613cb8613c4c565b613ccb601f8201601f1916602001613c60565b818152846020838601011115613cdf575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f8060808587031215613d0e575f80fd5b8435613d1981613c0e565b93506020850135613d2981613c0e565b92506040850135915060608501356001600160401b03811115613d4a575f80fd5b613d5687828801613c90565b91505092959194509250565b5f805f805f60a08688031215613d76575f80fd5b8535613d8181613c0e565b94506020860135613d9181613c0e565b9350604086013592506060860135613da881613c0e565b949793965091946080013592915050565b5f8083601f840112613dc9575f80fd5b5081356001600160401b03811115613ddf575f80fd5b6020830191508360208260051b85010111156127f8575f80fd5b5f805f805f8060608789031215613e0e575f80fd5b86356001600160401b0380821115613e24575f80fd5b613e308a838b01613db9565b90985096506020890135915080821115613e48575f80fd5b613e548a838b01613db9565b90965094506040890135915080821115613e6c575f80fd5b50613e7989828a01613db9565b979a9699509497509295939492505050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015613ede57603f19888603018452613ecc858351613bd1565b94509285019290850190600101613eb0565b5092979650505050505050565b5f805f60608486031215613efd575f80fd5b8335613f0881613c0e565b92506020840135613f1881613c0e565b929592945050506040919091013590565b5f60208284031215613f39575f80fd5b8135610e2481613c0e565b5f805f805f805f806080898b031215613f5b575f80fd5b88356001600160401b0380821115613f71575f80fd5b613f7d8c838d01613db9565b909a50985060208b0135915080821115613f95575f80fd5b613fa18c838d01613db9565b909850965060408b0135915080821115613fb9575f80fd5b613fc58c838d01613db9565b909650945060608b0135915080821115613fdd575f80fd5b50613fea8b828c01613db9565b999c989b5096995094979396929594505050565b602080825282518282018190525f919060409081850190868401855b82811015614050578151805165ffffffffffff1685528601516001600160801b031686850152928401929085019060010161401a565b5091979650505050505050565b5f806040838503121561406e575f80fd5b823561407981613c0e565b9150602083013561408981613c0e565b809150509250929050565b5f80602083850312156140a5575f80fd5b82356001600160401b038111156140ba575f80fd5b6140c685828601613db9565b90969095509350505050565b5f602082840312156140e2575f80fd5b813565ffffffffffff81168114610e24575f80fd5b5f805f60408486031215614109575f80fd5b83356001600160401b0381111561411e575f80fd5b61412a86828701613db9565b909450925050602084013561413e81613c0e565b809150509250925092565b5f82601f830112614158575f80fd5b813560206001600160401b0382111561417357614173613c4c565b8160051b614182828201613c60565b928352848101820192828101908785111561419b575f80fd5b83870192505b84831015611cda578235825291830191908301906141a1565b5f805f805f60a086880312156141ce575f80fd5b85356141d981613c0e565b945060208601356141e981613c0e565b935060408601356001600160401b0380821115614204575f80fd5b61421089838a01614149565b94506060880135915080821115614225575f80fd5b61423189838a01614149565b93506080880135915080821115614246575f80fd5b5061425388828901613c90565b9150509295509295909350565b5f805f805f805f60e0888a031215614276575f80fd5b873561428181613c0e565b9650602088013561429181613c0e565b95506040880135945060608801359350608088013560ff811681146142b4575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f805f805f60a086880312156142e5575f80fd5b85356142f081613c0e565b9450602086013561430081613c0e565b9350604086013592506060860135915060808601356001600160401b03811115614328575f80fd5b61425388828901613c90565b5f60208284031215614344575f80fd5b5035919050565b5f805f806060858703121561435e575f80fd5b843561436981613c0e565b935060208501356001600160401b0380821115614384575f80fd5b818701915087601f830112614397575f80fd5b8135818111156143a5575f80fd5b8860208285010111156143b6575f80fd5b95986020929092019750949560400135945092505050565b600181811c908216806143e257607f821691505b60208210810361440057634e487b7160e01b5f52602260045260245ffd5b50919050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614455575f80fd5b8301803591506001600160401b0382111561446e575f80fd5b6020019150368190038213156127f8575f80fd5b634e487b7160e01b5f52601160045260245ffd5b5f600182016144a7576144a7614482565b5060010190565b6001600160401b038281168282160390808211156144ce576144ce614482565b5092915050565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b5f60208284031215614512575f80fd5b81518015158114610e24575f80fd5b8082028115828204841417610a7957610a79614482565b80820180821115610a7957610a79614482565b5f65ffffffffffff80831681810361456557614565614482565b6001019392505050565b634e487b7160e01b5f52601260045260245ffd5b5f826145915761459161456f565b500490565b6001600160801b038181168382160190808211156144ce576144ce614482565b5f82516145c7818460208701613baf565b9190910192915050565b5f80835481600182811c9150808316806145ec57607f831692505b6020808410820361460b57634e487b7160e01b86526022600452602486fd5b81801561461f57600181146146345761465f565b60ff198616895284151585028901965061465f565b5f8a8152602090205f5b868110156146575781548b82015290850190830161463e565b505084890196505b509498975050505050505050565b606080825284519082018190525f906020906080840190828801845b828110156146ae5781516001600160a01b031684529284019290840190600101614689565b505050838103828501528551808252868301918301905f5b818110156146e2578351835292840192918401916001016146c6565b50506001600160a01b03861660408601529250610bc3915050565b65ffffffffffff8181168382160190808211156144ce576144ce614482565b81810381811115610a7957610a79614482565b6001600160801b038281168282160390808211156144ce576144ce614482565b634e487b7160e01b5f52603160045260245ffd5b5f826147715761477161456f565b500690565b65ffffffffffff8281168282160390808211156144ce576144ce61448256fe632056710afd218e9699548c7020044b84740417c6b482b438054907f72cceaeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220f7f293b82cf6c6f06c88df7d8f082e34f045ec4d595593166f52c065695ef13c64736f6c63430008150033

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

0000000000000000000000005f2f11ad8656439d5c14d9b351f8b09cdac2a02d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010526f79636f2055534443205661756c74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007726f795553444300000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _owner (address): 0x5F2F11ad8656439d5C14d9B351f8b09cDaC2A02d
Arg [1] : _name (string): Royco USDC Vault
Arg [2] : _symbol (string): royUSDC
Arg [3] : _decimals (uint8): 6

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000005f2f11ad8656439d5c14d9b351f8b09cdac2a02d
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000010
Arg [5] : 526f79636f2055534443205661756c7400000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [7] : 726f795553444300000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.