ETH Price: $1,982.77 (-4.45%)

Contract

0xC57a4B65fc95BefB4f29E81A03fF3FEb037D3B0D
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Balance Of245733052026-03-03 0:20:234 days ago1772497223IN
0xC57a4B65...b037D3B0D
0 ETH0.000001130.04698435
Claim245732862026-03-03 0:16:354 days ago1772496995IN
0xC57a4B65...b037D3B0D
0 ETH0.000000960.04421292

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
vExitQueue

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

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

import "utils.sol/Fixable.sol";
import "utils.sol/NFT.sol";
import "utils.sol/Implementation.sol";
import "utils.sol/types/bool.sol";
import "openzeppelin-contracts/utils/Base64.sol";

import "./lib/LibStringify.sol";
import "./ctypes/ticket_array.sol";
import "./ctypes/cask_array.sol";
import "./interfaces/IvExitQueue.sol";
import "./interfaces/IvPool.sol";
import "./interfaces/IvFactory.sol";

/// @title Exit Queue
/// @author mortimr @ Kiln
/// @notice The exit queue stores exit requests until they are filled and claimable
///
///           ⢀⣀ ⢀⣤⣤⣤⠄⣠⣤⣤⠄⢀⣀⡀
///         ⢀⣾⣿⠏⢰⣿⣿⣿⠃⣰⣿⣿⠁⣴⣿⣿⣿⣷⡀
///         ⣾⣿⡟⢀⣿⣿⣿⡏⢠⣿⣿⡇⢰⣿⣿⣿⣿⣿⣷
///        ⢸⣿⣿⡇⢸⣿⣿⣿⡇⢸⣿⣿ ⢸⣿⣿⣿⣿⣿⣿
///        ⠈⣿⣿⡇⠸⣿⣿⣿⡇⢸⣿⣿⡆⢸⣿⣿⠟⢿⣿⣿
///         ⠘⠛⠛ ⠛⠛⠛⠃⠈⠻⠿⠧⠈⣿⡇ ⢸⣿⠃
///      ⢀⣤⠄⢀⣶⣿⣿⡟⢠⣾⣿⠇⢀⣤⣤⣄⠛⠛⠛⢁ ⣤⣤⣄         ⢀⣤⠄⢀⣶⣿⣿⡟⢠⣾⣿⠇⢀⣤⣤⣄     ⣤⣤⣄
///     ⣴⣿⡏⢠⣿⣿⣿⡟⢠⣿⣿⠃⣰⣿⣿⣿⣿⣧  ⢠⣿⣿⣿⣿⣧      ⣴⣿⡏⢠⣿⣿⣿⡟⢠⣿⣿⠃⣰⣿⣿⣿⣿⣧  ⢠⣿⣿⣿⣿⣧
///    ⢰⣿⣿ ⣾⣿⣿⣿⠁⣼⣿⡏⢠⣿⣿⣿⣿⣿⣿⡄ ⣿⣿⣿⣿⣿⣿⡇    ⢰⣿⣿ ⣾⣿⣿⣿⠁⣼⣿⡏⢠⣿⣿⣿⣿⣿⣿⡄ ⣿⣿⣿⣿⣿⣿⡇
///    ⢸⣿⣿ ⣿⣿⣿⣿ ⣿⣿⡇⢸⣿⣿⣿⣿⣿⣿⡇ ⣿⣿⣿⣿⣿⣿⡇    ⢸⣿⣿ ⣿⣿⣿⣿ ⣿⣿⡇⢸⣿⣿⣿⣿⣿⣿⡇ ⣿⣿⣿⣿⣿⣿⡇
///    ⠘⣿⣿ ⣿⣿⣿⣿⡀⢻⣿⣧⠈⣿⣿⠟⠙⣿⣿⠁ ⢿⣿⡟⠙⢿⣿⠇    ⠘⣿⣿ ⣿⣿⣿⣿⡀⢻⣿⣧⠈⣿⣿⠟⠙⣿⣿⠁ ⢿⣿⡟⠙⢿⣿⠇
///     ⠙⢿⣇⠘⢿⣿⣿⣧⠈⣿⣿⣆⠘⣿⣄⣠⣾⠃⣰⣧⠘⢿⣇⢀⣾⠏      ⠙⢿⣇⠘⢿⣿⣿⣧⠈⣿⣿⣆⠘⣿⣄⣠⣾⠃⣰⣧⠘⢿⣇⢀⣾⠏
///       ⠈⠁⠈⠛⠛⠛⠃⠈⠛⠛⠓ ⠉⠉ ⠐⠛⠛⠓ ⠉⠉⠁          ⠈⠁⠈⠛⠛⠛⠃⠈⠛⠛⠓ ⠉⠉ ⠐⠛⠛⠓ ⠉⠉⠁
///
// slither-disable-next-line naming-convention
contract vExitQueue is NFT, Fixable, Initializable, Implementation, IvExitQueue {
    using LUint256 for types.Uint256;
    using LAddress for types.Address;
    using LBool for types.Bool;
    using LString for types.String;
    using LTicketArray for ctypes.TicketArray;
    using LCaskArray for ctypes.CaskArray;

    /// @dev Address of the associated vPool
    /// @dev Slot: keccak256(bytes("exitQueue.1.pool"))) - 1
    types.Address internal constant $pool = types.Address.wrap(0xdcdd87edea8fcbdc6d50bb4863c8269eed833245e48ec3e4f64dc4cd88a27283);

    /// @dev Total amount of unclaimed funds in the exit queue - 1
    /// @dev Slot: keccak256(bytes("exitQueue.1.unclaimedFunds")))
    types.Uint256 internal constant $unclaimedFunds = types.Uint256.wrap(0x51fae72b3be6f7b8c2f4de519c1a9fb3f8624c4c7d1f85109b6659ae4958c29a);

    /// @dev Flag enabling/disabling transfers
    /// @dev Slot: keccak256(bytes("exitQueue.1.transferEnabled"))) - 1
    types.Bool internal constant $transferEnabled = types.Bool.wrap(0xc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a0);

    /// @dev Token URI image URL
    /// @dev Slot: keccak256(bytes("exitQueue.1.tokenUriImageUrl"))) - 1
    types.String internal constant $tokenUriImageUrl = types.String.wrap(0x0f0463b3f5083af4c7135d28606a2c0eaa2bd9e3f9f62db1539e47244df8dc49);

    /// @dev Array of tickets
    /// @dev Slot: keccak256(bytes("exitQueue.1.tickets"))) - 1
    ctypes.TicketArray internal constant $tickets =
        ctypes.TicketArray.wrap(0x409fdfd8838fda00128ca5d502af2ba15c034ca4130776e2ed6d3eb7811e3481);

    /// @dev Array of casks
    /// @dev Slot: keccak256(bytes("exitQueue.1.casks"))) - 1
    ctypes.CaskArray internal constant $casks = ctypes.CaskArray.wrap(0x39a5c864ceb6f99a196a385a148476994e3952fd6d71d040a2339a143eaeabe1);

    /// @dev Resolution error code for a ticket that is out of bounds
    int64 internal constant TICKET_ID_OUT_OF_BOUNDS = -1;
    /// @dev Resolution error code for a ticket that has already been claimed
    int64 internal constant TICKET_ALREADY_CLAIMED = -2;
    /// @dev Resolution error code for a ticket that is pending fulfillment
    int64 internal constant TICKET_PENDING = -3;

    /// @notice Prevents calls not coming from the associated vPool
    modifier onlyPool() {
        if (msg.sender != $pool.get()) {
            revert LibErrors.Unauthorized(msg.sender, $pool.get());
        }
        _;
    }

    /// @notice Prevents calls not coming from the vFactory admin
    modifier onlyAdmin() {
        {
            address admin = IvFactory(_castedPool().factory()).admin();
            if (msg.sender != admin) {
                revert LibErrors.Unauthorized(msg.sender, admin);
            }
        }
        _;
    }

    /// @inheritdoc IvExitQueue
    // slither-disable-next-line missing-zero-check
    function initialize(address vpool, string calldata newTokenUriImageUrl) external init(0) {
        _setTokenUriImageUrl(newTokenUriImageUrl);
        LibSanitize.notZeroAddress(vpool);
        $pool.set(vpool);
        emit SetPool(vpool);
    }

    /// @inheritdoc IvExitQueue
    function tokenUriImageUrl() external view returns (string memory) {
        return $tokenUriImageUrl.get();
    }

    /// @notice Get the Exit Queue name from the associated vPool -> Factory.
    /// @dev The name is mutable (can be updated by the Factory admin).
    /// @return The name of the Exit Queue.
    function name() external view override returns (string memory) {
        // slither-disable-next-line unused-return
        (string memory operatorName,,) = IvFactory(_castedPool().factory()).metadata();
        return string(abi.encodePacked(operatorName, " Exit Queue"));
    }

    /// @inheritdoc NFT
    function symbol() external pure override returns (string memory) {
        return "vEQ";
    }

    /// @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view override(NFT) returns (string memory) {
        _requireExists(tokenId);
        uint256 tokenIdx = _getTicketIdx(tokenId);
        ctypes.Ticket memory t = $tickets.get()[tokenIdx];
        ctypes.Cask[] storage caskArray = $casks.get();
        uint256 claimable = 0;
        uint256 queueSize = 0;
        {
            ctypes.Cask memory c = caskArray.length > 0 ? caskArray[caskArray.length - 1] : ctypes.Cask({position: 0, size: 0, value: 0});

            //           | CASK |
            // | TICKET |
            if (c.position > t.position + t.size) {
                claimable = t.size;
                //       | CASK |
                // | TICKET |
            } else if (c.position >= t.position) {
                claimable = t.size - (c.position + c.size >= t.position + t.size ? 0 : (t.position + t.size) - (c.position + c.size));
                // | CASK |
                //      | TICKET |
            } else if (c.position < t.position && t.position < c.position + c.size) {
                claimable = (c.position + c.size) - t.position;
            }
            queueSize = c.position + c.size;
        }
        bytes memory fullImageUrl =
            abi.encodePacked($tokenUriImageUrl.get(), "/", Strings.toHexString(address(this)), "/", Strings.toString(tokenId));
        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(
                    abi.encodePacked(
                        "{",
                        "\"name\":\"Exit Ticket #",
                        Strings.toString(tokenIdx),
                        "\",",
                        "\"description\":\"This exit ticket can be used to claim funds from the exit queue contract once it is fulfilled.\",",
                        _generateAttributes(t, claimable, queueSize),
                        "\"image_url\":\"",
                        fullImageUrl,
                        "\"}"
                    )
                )
            )
        );
    }

    /// @inheritdoc IvExitQueue
    function transferEnabled() external view returns (bool) {
        return $transferEnabled.get();
    }

    /// @inheritdoc IvExitQueue
    function unclaimedFunds() external view returns (uint256) {
        return $unclaimedFunds.get();
    }

    /// @inheritdoc IvExitQueue
    function ticketIdAtIndex(uint32 idx) external view returns (uint256) {
        return _getTicketId(idx, $tickets.get()[idx]);
    }

    /// @inheritdoc IvExitQueue
    function ticket(uint256 id) external view returns (ctypes.Ticket memory) {
        uint256 idx = _getTicketIdx(id);
        ctypes.Ticket[] storage ticketArray = $tickets.get();
        if (idx >= ticketArray.length) {
            revert InvalidTicketId(id);
        }
        return ticketArray[idx];
    }

    /// @inheritdoc IvExitQueue
    function ticketCount() external view returns (uint256) {
        return $tickets.get().length;
    }

    /// @inheritdoc IvExitQueue
    function cask(uint32 id) external view returns (ctypes.Cask memory) {
        ctypes.Cask[] storage caskArray = $casks.get();
        if (id >= caskArray.length) {
            revert InvalidCaskId(id);
        }
        return caskArray[id];
    }

    /// @inheritdoc IvExitQueue
    function caskCount() external view returns (uint256) {
        return $casks.get().length;
    }

    /// @inheritdoc IvExitQueue
    function resolve(uint256[] memory ticketIds) external view returns (int64[] memory caskIdsOrErrors) {
        uint256 ticketIdsLength = ticketIds.length;
        caskIdsOrErrors = new int64[](ticketIdsLength);
        uint256 totalTicketCount = $tickets.get().length;
        uint256 totalCaskCount = $casks.get().length;
        for (uint256 idx = 0; idx < ticketIdsLength;) {
            caskIdsOrErrors[idx] = _resolve(ticketIds[idx], totalTicketCount, totalCaskCount);
            unchecked {
                ++idx;
            }
        }
    }

    /// @inheritdoc IvExitQueue
    function feed(uint256 shares) external payable onlyPool {
        LibSanitize.notNullValue(shares);
        LibSanitize.notNullValue(msg.value);
        ctypes.Cask[] storage caskArray = $casks.get();
        uint256 casksLength = caskArray.length;
        ctypes.Cask memory lastCask = casksLength > 0 ? caskArray[casksLength - 1] : ctypes.Cask({position: 0, size: 0, value: 0});
        ctypes.Cask memory newCask =
            ctypes.Cask({position: lastCask.position + lastCask.size, size: uint128(shares), value: uint128(msg.value)});
        caskArray.push(newCask);
        emit ReceivedCask(uint32(casksLength), newCask);
    }

    /// @inheritdoc IvExitQueue
    function pull(uint256 max) external onlyPool {
        uint256 currentUnclaimedFunds = $unclaimedFunds.get();
        uint256 maxPullable = LibUint256.min(max, currentUnclaimedFunds);
        if (maxPullable > 0) {
            _setUnclaimedFunds(currentUnclaimedFunds - maxPullable);
            emit SuppliedEther(maxPullable);
            _castedPool().injectEther{value: maxPullable}();
        }
    }

    /// @inheritdoc IvPoolSharesReceiver
    //slither-disable-next-line assembly
    function onvPoolSharesReceived(address, address from, uint256 amount, bytes memory data) external override onlyPool returns (bytes4) {
        LibSanitize.notNullValue(amount);
        if (data.length == 20) {
            // If the data appears to be a packed encoded address we print a ticket to that address instead of the sender
            address to;
            assembly {
                // After skipping the length element of data, the first element (20 bytes padded on the right to 32 bytes) is
                // converted to an actual address by right shifting.
                to := shr(96, mload(add(data, 32)))
            }
            _printTicket(uint128(amount), to);
        } else {
            _printTicket(uint128(amount), from);
        }
        return IvPoolSharesReceiver.onvPoolSharesReceived.selector;
    }

    /// @inheritdoc IvExitQueue
    function setTokenUriImageUrl(string calldata newTokenUriImageUrl) external onlyAdmin {
        _setTokenUriImageUrl(newTokenUriImageUrl);
    }

    /// @inheritdoc IvExitQueue
    function setTransferEnabled(bool value) external onlyAdmin {
        _setTransferEnabled(value);
    }

    struct ClaimInternalVariables {
        uint256 ticketIdsLength;
        uint256 totalTicketCount;
        address[] recipients;
        uint256[] payments;
        uint256 usedRecipients;
        ConsumeTicketParameters params;
    }

    /// @inheritdoc IvExitQueue
    // slither-disable-next-line arbitrary-send-eth,calls-loop,reentrancy-events,cyclomatic-complexity
    function claim(uint256[] calldata ticketIds, uint32[] calldata caskIds, uint16 maxClaimDepth)
        external
        returns (ClaimStatus[] memory statuses)
    {
        // slither-disable-next-line uninitialized-local
        ClaimInternalVariables memory __;
        __.ticketIdsLength = ticketIds.length;
        if (__.ticketIdsLength == 0 || __.ticketIdsLength != caskIds.length) {
            revert InvalidLengths();
        }
        __.totalTicketCount = $tickets.get().length;
        __.params.totalCaskCount = $casks.get().length;

        statuses = new ClaimStatus[](ticketIds.length);

        __.recipients = new address[](ticketIds.length);
        __.payments = new uint256[](ticketIds.length);
        __.usedRecipients = 0;

        // slither-disable-next-line uninitialized-local
        for (uint256 idx; idx < __.ticketIdsLength;) {
            __.params.ticketId = ticketIds[idx];
            __.params.ticketIdx = _getTicketIdx(__.params.ticketId);
            __.params.caskId = caskIds[idx];
            __.params.depth = maxClaimDepth;
            __.params.ethToPay = 0;

            // this line reverts if the ticket id doesn't exist
            address owner = ownerOf(ticketIds[idx]);

            __.params.t = $tickets.get()[__.params.ticketIdx];

            if (__.params.t.size == 0) {
                statuses[idx] = ClaimStatus.SKIPPED;
                unchecked {
                    ++idx;
                }
                continue;
            }

            if (__.params.caskId >= __.params.totalCaskCount) {
                revert InvalidCaskId(__.params.caskId);
            }

            __.params.c = $casks.get()[__.params.caskId];

            if (!_matching(__.params.t, __.params.c)) {
                revert TicketNotMatchingCask(__.params.ticketId, __.params.caskId);
            }

            _consumeTicket(__.params);

            if (__.params.t.size > 0) {
                uint256 ticketIdx = _getTicketIdx(ticketIds[idx]);
                _burn(ticketIds[idx]);
                uint256 newTicketId = _getTicketId(ticketIdx, $tickets.get()[ticketIdx]);
                _mint(owner, newTicketId);
                emit TicketIdUpdated(ticketIds[idx], newTicketId, uint32(ticketIdx));
            }
            statuses[idx] = __.params.t.size > 0 ? ClaimStatus.PARTIALLY_CLAIMED : ClaimStatus.CLAIMED;
            if (__.params.ethToPay > 0) {
                int256 ownerIndex = -1;
                for (uint256 recipientIdx = 0; recipientIdx < __.usedRecipients;) {
                    if (__.recipients[recipientIdx] == owner) {
                        ownerIndex = int256(recipientIdx);
                        break;
                    }
                    unchecked {
                        ++recipientIdx;
                    }
                }

                if (ownerIndex == -1) {
                    __.recipients[__.usedRecipients] = owner;
                    __.payments[__.usedRecipients] = __.params.ethToPay;
                    unchecked {
                        ++__.usedRecipients;
                    }
                } else {
                    __.payments[uint256(ownerIndex)] += __.params.ethToPay;
                }
            }

            unchecked {
                ++idx;
            }
        }

        for (uint256 recipientIdx = 0; recipientIdx < __.usedRecipients;) {
            address recipient = __.recipients[recipientIdx];
            uint256 payment = __.payments[recipientIdx];
            // slither-disable-next-line missing-zero-check,low-level-calls
            (bool success, bytes memory reason) = recipient.call{value: payment}("");
            if (!success) {
                revert ClaimTransferFailed(recipient, reason);
            }
            emit Payment(recipient, payment);
            unchecked {
                ++recipientIdx;
            }
        }
    }

    /// @dev Internal utility function to retrieve the string status of a ticket
    /// @param t The ticket to get the status of
    /// @param claimable The amount of the ticket that is claimable
    /// @return The status of the ticket
    function _getStatusString(ctypes.Ticket memory t, uint256 claimable) internal pure returns (string memory) {
        if (claimable == 0) {
            return "Not yet claimable";
        } else if (claimable < t.size) {
            return "Partially claimable";
        }
        return "Fully claimable";
    }

    /// @dev Internal utility function to generate the attributes of a ticket
    /// @param t The ticket to get the attributes of
    /// @param claimable The amount of the ticket that is claimable
    /// @return The attributes of the ticket
    function _generateAttributes(ctypes.Ticket memory t, uint256 claimable, uint256 queueSize) internal pure returns (bytes memory) {
        return abi.encodePacked(
            "\"attributes\":[{\"trait_type\":\"Queue position\",\"value\":",
            LibStringify.uintToDecimalString(t.position, 18, 3),
            ",\"display_type\":\"number\",\"max_value\":",
            LibStringify.uintToDecimalString(queueSize, 18, 3),
            "},{\"trait_type\":\"Claimable amount\",\"value\":",
            LibStringify.uintToDecimalString(claimable, 18, 3),
            ",\"display_type\":\"number\",\"max_value\":",
            LibStringify.uintToDecimalString(t.size, 18, 3),
            "},{\"trait_type\":\"Status\",\"value\":\"",
            _getStatusString(t, claimable),
            "\"}],"
        );
    }

    /// @dev Internal hook happening at each transfer.
    ///      To override.
    function _onTransfer(address, address, uint256) internal view override {
        if (!$transferEnabled.get()) {
            revert TransferDisabled();
        }
    }

    /// @dev Internal hook happening at each mint.
    ///      To override.
    /// @param to The address receiving the token
    /// @param tokenId The token id
    function _onMint(address to, uint256 tokenId) internal override {}

    /// @dev Internal hook happening at each burn.
    ///      To override.
    /// @param tokenId The token id
    function _onBurn(uint256 tokenId) internal override {}

    /// @dev Internal utility to retrieve the vPool address casted to the vPool interface
    /// @return The vPool address casted to the vPool interface
    function _castedPool() internal view returns (IvPool) {
        return IvPool($pool.get());
    }

    /// @dev Internal utility to check if a ticket is claimable on a cask
    /// @param t The ticket to check
    /// @param p The cask to check
    /// @return True if the ticket is claimable on the cask
    function _matching(ctypes.Ticket memory t, ctypes.Cask memory p) internal pure returns (bool) {
        return (t.position < p.position + p.size && t.position >= p.position);
    }

    /// @dev Internal utility to perform a dichotomy search to find the cask matching a ticket
    /// @param ticketIdx The index of the ticket to find the cask for
    /// @return caskId The cask id matching the ticket
    function _searchCaskForTicket(uint256 ticketIdx) internal view returns (uint32 caskId) {
        ctypes.Cask[] storage caskArray = $casks.get();
        uint32 right = uint32(caskArray.length - 1);

        ctypes.Ticket memory t = $tickets.get()[ticketIdx];

        if (_matching(t, caskArray[right])) {
            return right;
        }

        uint32 left = 0;

        if (_matching(t, caskArray[left])) {
            return left;
        }

        while (left != right) {
            uint32 middle = (left + right) >> 1;

            ctypes.Cask memory middleC = caskArray[middle];
            if (_matching(t, middleC)) {
                return middle;
            }

            if (t.position < middleC.position) {
                right = middle;
            } else {
                left = middle;
            }
        }
        return left;
    }

    /// @dev Internal utility to resolve a ticket
    /// @param ticketId The ticket to resolve
    /// @param totalTicketCount The total number of tickets
    /// @param totalCaskCount The total number of casks
    /// @return caskIdOrError The cask id matching the ticket or an error code
    function _resolve(uint256 ticketId, uint256 totalTicketCount, uint256 totalCaskCount) internal view returns (int64 caskIdOrError) {
        uint256 ticketIdx = _getTicketIdx(ticketId);
        if (ticketIdx >= totalTicketCount) {
            return TICKET_ID_OUT_OF_BOUNDS;
        }
        ctypes.Ticket memory t = $tickets.get()[ticketIdx];
        if (t.size == 0) {
            return TICKET_ALREADY_CLAIMED;
        }
        if (totalCaskCount == 0 || $casks.get()[totalCaskCount - 1].position + $casks.get()[totalCaskCount - 1].size <= t.position) {
            return TICKET_PENDING;
        }
        return int64(uint64(_searchCaskForTicket(ticketIdx)));
    }

    /// @dev Retrieves the ticket id from its index and size.
    ///      The ticket id is dynamic, every time someone performs a partial claim of the ticket, its id changes.
    ///      If the claim is complete, the ticket is burned. This would lower secondary market attack vectors that
    ///      include claiming before selling.
    /// @param ticketIndex The index of the ticket
    /// @param t The ticket
    /// @return The ticket id
    function _getTicketId(uint256 ticketIndex, ctypes.Ticket memory t) internal pure returns (uint256) {
        return ticketIndex << 128 | uint256(t.size);
    }

    /// @dev Retrieves the ticket index from its id
    /// @param ticketId The ticket id
    /// @return The ticket index
    function _getTicketIdx(uint256 ticketId) internal pure returns (uint256) {
        return ticketId >> 128;
    }

    /// @dev Internal utility to create a new ticket
    /// @param amount The amount of shares in the ticket
    /// @param owner The owner of the ticket
    function _printTicket(uint128 amount, address owner) internal {
        IvPool pool = _castedPool();
        uint256 totalUnderlyingSupply = pool.totalUnderlyingSupply();
        uint256 totalSupply = pool.totalSupply();
        ctypes.Ticket[] storage ticketArray = $tickets.get();
        uint256 ticketsLength = ticketArray.length;
        ctypes.Ticket memory lastTicket =
            ticketArray.length > 0 ? ticketArray[ticketsLength - 1] : ctypes.Ticket({position: 0, size: 0, maxExitable: 0});
        ctypes.Ticket memory newTicket = ctypes.Ticket({
            position: lastTicket.position + lastTicket.size,
            size: amount,
            maxExitable: uint128(LibUint256.mulDiv(amount, totalUnderlyingSupply, totalSupply))
        });
        uint256 ticketId = _getTicketId(ticketsLength, newTicket);
        ticketArray.push(newTicket);
        _mint(owner, ticketId);
        emit PrintedTicket(owner, uint32(ticketsLength), ticketId, newTicket);
    }

    /// @notice The parameters of the consume call
    /// @param ticketId The ticket id
    /// @param ticketIdx The index of the ticket
    /// @param t The ticket itself
    /// @param caskId The cask id
    /// @param c The cask itself
    /// @param totalCaskCount The total number of casks
    /// @param depth The initial depth of the consume call
    /// @param ethToPay The resulting eth to pay the user
    struct ConsumeTicketParameters {
        uint256 ticketId;
        uint256 ticketIdx;
        ctypes.Ticket t;
        uint32 caskId;
        ctypes.Cask c;
        uint256 totalCaskCount;
        uint16 depth;
        uint256 ethToPay;
    }

    /// @dev Internal utility to consume a ticket.
    ///      Will call itself recursively to consume the ticket on all the casks it overlaps.
    ///      Recursive calls are limited to the initial depth.
    /// @param params The parameters of the consume call
    function _consumeTicket(ConsumeTicketParameters memory params) internal {
        // we compute the end position of the cask
        uint256 caskEnd = params.c.position + params.c.size;

        // we compute the amount of shares and eth that overlap between the ticket and the cask
        uint128 overlappingAmount = uint128(LibUint256.min(params.t.size, caskEnd - params.t.position));
        uint128 overlappingEthAmount = uint128(LibUint256.mulDiv(overlappingAmount, params.c.value, params.c.size));
        uint128 maxRedeemableEthAmount = uint128(LibUint256.mulDiv(overlappingAmount, params.t.maxExitable, params.t.size));

        // we initialize the unclaimable amount to 0 before checking if the ticket is exceeding the capped ticket rate
        uint256 unclaimableAmount = 0;

        // then we check if the overlapping amount is not exceeding the capped ticket rate
        // and if it's the case we adjust the amount of eth we can pay
        if (maxRedeemableEthAmount < overlappingEthAmount) {
            unclaimableAmount = overlappingEthAmount - maxRedeemableEthAmount;
            overlappingEthAmount = maxRedeemableEthAmount;
            _setUnclaimedFunds($unclaimedFunds.get() + unclaimableAmount);
        }

        // we update the ticket in memory
        params.t.position += overlappingAmount;
        params.t.size -= overlappingAmount;
        params.t.maxExitable -= overlappingEthAmount;

        // we update the total to pay for this ticket
        params.ethToPay += overlappingEthAmount;

        // we log the step
        emit FilledTicket(params.ticketId, params.caskId, uint128(overlappingAmount), overlappingEthAmount, unclaimableAmount);

        // if
        // - the ticket is not empty
        // - there are more casks to consume
        // - we are not at the maximum depth
        // then we call this method recursively with the next cask
        // otherwise we update the ticket in storage and burn it if it's empty
        if (params.t.size > 0 && params.caskId + 1 < params.totalCaskCount && params.depth > 0) {
            params.caskId += 1;
            params.c = $casks.get()[params.caskId];
            params.depth -= 1;
            _consumeTicket(params);
        } else {
            if (params.t.size == 0) {
                _burn(params.ticketId);
            }
            ctypes.Ticket[] storage ticketArray = $tickets.get();
            ticketArray[params.ticketIdx] = params.t;
        }
    }

    /// @dev Internal utility to set the unclaimed funds buffer
    /// @param newUnclaimedFunds The new unclaimed funds buffer
    function _setUnclaimedFunds(uint256 newUnclaimedFunds) internal {
        $unclaimedFunds.set(newUnclaimedFunds);
        emit SetUnclaimedFunds(newUnclaimedFunds);
    }

    /// @dev Internal utility to set the transfer enabled flag
    /// @param value The new transfer enabled flag
    function _setTransferEnabled(bool value) internal {
        $transferEnabled.set(value);
        emit SetTransferEnabled(value);
    }

    /// @dev Internal utility to set the token URI image URL
    /// @param newTokenUriImageUrl The new token URI image URL
    function _setTokenUriImageUrl(string calldata newTokenUriImageUrl) internal {
        LibSanitize.notEmptyString(newTokenUriImageUrl);
        $tokenUriImageUrl.set(newTokenUriImageUrl);
        emit SetTokenUriImageUrl($tokenUriImageUrl.get());
    }
}

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

import "./interfaces/IFixable.sol";

/// @title Fixable
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice The Fixable contract can be used on cubs to expose a safe noop to force a fix.
abstract contract Fixable is IFixable {
    /// @inheritdoc IFixable
    function fix() external {}
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import "openzeppelin-contracts/token/ERC721/IERC721Receiver.sol";

import "./types/address.sol";
import "./types/string.sol";
import "./types/mapping.sol";
import "./libs/LibSanitize.sol";
import "./Initializable.sol";
import "./interfaces/INFT.sol";

import "./uctypes/operator_approvals.sol";

/// @title NFT
/// @author mortimr @ Kiln
/// @notice NFT contract using utils.sol storage format.
// slither-disable-next-line unimplemented-functions
abstract contract NFT is INFT {
    using LString for types.String;
    using LUint256 for types.Uint256;
    using LMapping for types.Mapping;
    using LOperatorApprovalsMapping for uctypes.OperatorApprovalsMapping;

    using CUint256 for uint256;
    using CAddress for address;

    /// @dev ERC721 name of the contract.
    /// @dev Slot: keccak256(bytes("nft.1.name")) - 1
    types.String internal constant $name =
        types.String.wrap(0x8be0d77374e3002afd46fd09ae2c8e3afc7315322504f7f1a09d189f4925e72f);
    /// @dev ERC721 symbol of the contract.
    /// @dev Slot: keccak256(bytes("nft.1.symbol")) - 1
    types.String internal constant $symbol =
        types.String.wrap(0xddad2df2277e0186b34991db0b7ceafa36b49b76d0a1e87f6e4d44b6b17a207f);
    /// @dev Internal ID counter to keep track of minted tokens.
    /// @dev Slot: keccak256(bytes("nft.1.mintCounter")) - 1
    types.Uint256 internal constant $mintCounter =
        types.Uint256.wrap(0x3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a1);
    /// @dev Internal burn counter used to keep track of the total supply.
    /// @dev Slot: keccak256(bytes("nft.1.burnCounter")) - 1
    types.Uint256 internal constant $burnCounter =
        types.Uint256.wrap(0x0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc2);
    /// @dev Internal mapping that holds the links between owners and NFT IDs.
    /// @dev Type: mapping (uint256 => address)
    /// @dev Slot: keccak256(bytes("nft.1.owners")) - 1
    types.Mapping internal constant $owners =
        types.Mapping.wrap(0xc1f66d46ebf7070ef20209d66f741219b00fb896714319503d158a28b0d103d3);
    /// @dev Internal mapping that holds the balances of every owner (how many NFTs they own).
    /// @dev Type: mapping (address => uint256)
    /// @dev Slot: keccak256(bytes("nft.1.balances")) - 1
    types.Mapping internal constant $balances =
        types.Mapping.wrap(0xf9245bc1df90ea86e77b9f2423fe9cc12aa083c8ab9a55e727b285192b30d98a);
    /// @dev Internal mapping that holds the token approval data.
    /// @dev Type: mapping (uint256 => address)
    /// @dev Slot: keccak256(bytes("nft.1.tokenApprovals")) - 1
    types.Mapping internal constant $tokenApprovals =
        types.Mapping.wrap(0x3790264503275ecd52e8f0b419eb5ce016ca8a1f0fbac5a9ede429d0c1732004);
    /// @dev Internal mapping of operator approvals.
    /// @dev Type: mapping (address => mapping (address => bool))
    /// @dev Slot: keccak256(bytes("nft.1.operatorApprovals")) - 1
    uctypes.OperatorApprovalsMapping internal constant $operatorApprovals =
        uctypes.OperatorApprovalsMapping.wrap(0x6c716a91f6b5f5a0aa2affaf44bd88ea94ec69e363cf1fe9251e00a0fcc6c34e);

    /// @dev Internal initializer to call when first deploying the contract.
    // slither-disable-next-line dead-code
    function initializeNFT(string memory name_, string memory symbol_) internal {
        _setName(name_);
        _setSymbol(symbol_);
    }

    /// @notice Returns the token uri for the given token id.
    /// @dev To override
    /// @param tokenId The token id to query
    function tokenURI(uint256 tokenId) external view virtual returns (string memory);

    /// @notice Internal hook happening at each transfer. Not called during mint or burn. Use _onMint and _onBurn instead.
    ///         The hook is called before state transitions are made.
    /// @dev To override
    /// @param from The address sending the token
    /// @param to The address receiving the token
    /// @param tokenId The token id
    function _onTransfer(address from, address to, uint256 tokenId) internal virtual;

    /// @notice Internal hook happening at each mint.
    ///         The hook is called before state transitions are made.
    /// @dev To override
    /// @param to The address receiving the token
    /// @param tokenId The token id
    function _onMint(address to, uint256 tokenId) internal virtual;

    /// @notice Internal hook happening at each burn.
    ///         The hook is called before state transitions are made.
    /// @dev To override
    /// @param tokenId The token id
    function _onBurn(uint256 tokenId) internal virtual;

    /// @inheritdoc INFT
    function totalSupply() external view returns (uint256) {
        return $mintCounter.get() - $burnCounter.get();
    }

    /// @inheritdoc IERC721
    function balanceOf(address owner) public view virtual override returns (uint256) {
        LibSanitize.notZeroAddress(owner);
        return $balances.get()[owner.k()];
    }

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert InvalidTokenId(tokenId);
        }
        return owner;
    }

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

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

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId;
    }

    /// @inheritdoc IERC721
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = _ownerOf(tokenId);
        if (to == owner) {
            revert ApprovalToOwner(owner);
        }

        if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) {
            revert LibErrors.Unauthorized(msg.sender, owner);
        }

        _approve(to, owner, tokenId);
    }

    /// @inheritdoc IERC721
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireExists(tokenId);

        return $tokenApprovals.get()[tokenId].toAddress();
    }

    /// @inheritdoc IERC721
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(msg.sender, operator, approved);
    }

    /// @inheritdoc IERC721
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return $operatorApprovals.get()[owner][operator];
    }

    /// @inheritdoc IERC721
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        if (!_isApprovedOrOwner(msg.sender, tokenId)) {
            revert LibErrors.Unauthorized(msg.sender, _ownerOf(tokenId));
        }
        _transfer(from, to, tokenId);
    }

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
        if (!_isApprovedOrOwner(msg.sender, tokenId)) {
            revert LibErrors.Unauthorized(msg.sender, _ownerOf(tokenId));
        }
        _safeTransfer(from, to, tokenId, data);
    }

    /// @dev Internal utility to set the ERC721 name value.
    /// @param newName The new name to set
    // slither-disable-next-line dead-code
    function _setName(string memory newName) internal {
        LibSanitize.notEmptyString(newName);
        $name.set(newName);
        emit SetName(newName);
    }

    /// @dev Internal utility to set the ERC721 symbol value.
    /// @param newSymbol The new symbol to set
    // slither-disable-next-line dead-code
    function _setSymbol(string memory newSymbol) internal {
        LibSanitize.notEmptyString(newSymbol);
        $symbol.set(newSymbol);
        emit SetSymbol(newSymbol);
    }

    /// @dev Internal utility to perform a safe transfer (transfer + extra checks on contracts).
    /// @param from The address sending the token
    /// @param to The address receiving the token
    /// @param tokenId The ID of the token
    /// @param data The extra data provided to contract callback calls
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        if (!_checkOnERC721Received(from, to, tokenId, data)) {
            revert NonERC721ReceiverTransfer(from, to, tokenId, data);
        }
    }

    /// @dev Internal utility to retrieve the owner of the specified token id.
    /// @param tokenId The token id to lookup
    /// @return The address of the token owner
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return $owners.get()[tokenId].toAddress();
    }

    /// @dev Internal utility to verify if a token id exists.
    /// @param tokenId The token id to verify
    /// @return True if exists
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /// @dev Internal utility to check if the specified address is either approved by the owner or the owner for the given token id.
    /// @param spender The address to verify
    /// @param tokenId The token id to verify
    /// @return True if approved or owner
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = _ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /// @dev Internal utility to perform a safe mint operation (mint + extra checks on contracts).
    /// @param to The address receiving the token
    /// @param tokenId The token id to create
    /// @param data The xtra data provided to contract callback calls
    // slither-disable-next-line dead-code
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        if (!_checkOnERC721Received(address(0), to, tokenId, data)) {
            revert NonERC721ReceiverTransfer(address(0), to, tokenId, data);
        }
    }

    /// @dev Internal utility to mint the desired token id.
    /// @param to The address that receives the token id
    /// @param tokenId The token id to create
    // slither-disable-next-line dead-code
    function _mint(address to, uint256 tokenId) internal virtual {
        if (to == address(0)) {
            revert IllegalMintToZero();
        }

        if (_exists(tokenId)) {
            revert TokenAlreadyMinted(tokenId);
        }

        _onMint(to, tokenId);

        if (_exists(tokenId)) {
            revert TokenAlreadyMinted(tokenId);
        }

        unchecked {
            // increase owner balance
            $balances.get()[to.k()] += 1;
            // increase global mint counter
            $mintCounter.set($mintCounter.get() + 1);
        }

        // set owner
        $owners.get()[tokenId] = to.v();

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

    /// @dev Internal utility to burn the desired token id.
    /// @param tokenId The token id to burn
    // slither-disable-next-line dead-code
    function _burn(uint256 tokenId) internal virtual {
        _requireExists(tokenId);
        _onBurn(tokenId);
        _requireExists(tokenId);

        address from = $owners.get()[tokenId].toAddress();

        unchecked {
            // decrease owner balance
            $balances.get()[from.k()] -= 1;
            // increase global burn counter
            $burnCounter.set($burnCounter.get() + 1);
        }

        // clear owner and approvals
        delete $tokenApprovals.get()[tokenId];
        delete $owners.get()[tokenId];

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

    /// @dev Internal utility to perform a regular transfer of a token.
    /// @param from The address sending the token
    /// @param to The address receiving the token
    /// @param tokenId The tokenId to transfer
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        if (to == address(0)) {
            revert IllegalTransferToZero();
        }

        if (_ownerOf(tokenId) != from) {
            revert LibErrors.Unauthorized(_ownerOf(tokenId), from);
        }

        _onTransfer(from, to, tokenId);

        if (_ownerOf(tokenId) != from) {
            revert LibErrors.Unauthorized(_ownerOf(tokenId), from);
        }

        // Clear approvals from the previous owner
        delete $tokenApprovals.get()[tokenId];

        unchecked {
            $balances.get()[from.k()] -= 1;
            $balances.get()[to.k()] += 1;
        }
        $owners.get()[tokenId] = to.v();

        emit Transfer(from, to, tokenId);
    }

    /// @dev Internal utility to approve an account for a token id.
    /// @param to The address receiving the approval
    /// @param owner The owner of the token id
    /// @param tokenId The token id
    function _approve(address to, address owner, uint256 tokenId) internal virtual {
        $tokenApprovals.get()[tokenId] = to.v();
        emit Approval(owner, to, tokenId);
    }

    /// @dev Internal utility to approve an account for all tokens of another account.
    /// @param owner The address owning the tokens
    /// @param operator The address receiving the approval
    /// @param approved True if approved, false otherwise
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (owner == operator) {
            revert ApprovalToOwner(owner);
        }
        $operatorApprovals.get()[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /// @dev Internal utility to check and revert if a token doesn't exists.
    /// @param tokenId The token id to verify
    function _requireExists(uint256 tokenId) internal view virtual {
        if (!_exists(tokenId)) {
            revert InvalidTokenId(tokenId);
        }
    }

    /// @dev Internal utility to perform checks upon transfer in the case of sending to a contract.
    /// @param from The address sending the token
    /// @param to The address receiving the token
    /// @param data The extra data to provide in the case where to is a contract
    /// @return True if all checks are good
    // slither-disable-next-line variable-scope,calls-loop,unused-return
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data)
        private
        returns (bool)
    {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert NonERC721ReceiverTransfer(from, to, tokenId, data);
                } else {
                    // slither-disable-next-line assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }
}

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

import "./types/uint256.sol";

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

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

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

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

import "./types.sol";

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

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

import "utils.sol/libs/LibUint256.sol";
import "utils.sol/libs/LibBytes.sol";
import "openzeppelin-contracts/utils/Strings.sol";

/// @title Stringify Library  - A library for converting numbers to strings with decimal support
library LibStringify {
    /// @dev Generates a string in memory with the requested count of zeroes
    /// @param count The number of zeroes to generate
    /// @return The generated string
    function generateZeroes(uint256 count) internal pure returns (string memory) {
        bytes memory zeroes = new bytes(count);
        for (uint256 idx = 0; idx < count;) {
            zeroes[idx] = "0";
            unchecked {
                ++idx;
            }
        }
        return string(zeroes);
    }

    /// @dev Converts a uint256 to a string with the requested number of decimals
    /// @param value The value to convert
    /// @param decimals The number of decimals to include
    /// @param maxIncludedDecimals The maximum number of decimals to include
    /// @return The generated string
    function uintToDecimalString(uint256 value, uint8 decimals, uint8 maxIncludedDecimals) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        bytes memory uintToString = bytes(Strings.toString(value));
        if (decimals == 0) {
            return string(uintToString);
        }
        uint256 len = uintToString.length;
        if (len > decimals) {
            if (maxIncludedDecimals == 0) {
                return string(LibBytes.slice(bytes(uintToString), 0, len - decimals));
            }
            uintToString = abi.encodePacked(
                LibBytes.slice(bytes(uintToString), 0, len - decimals),
                ".",
                LibBytes.slice(bytes(uintToString), len - decimals, LibUint256.min(decimals, maxIncludedDecimals))
            );
        } else {
            if (maxIncludedDecimals <= decimals - len) {
                return "0";
            }
            uintToString = abi.encodePacked(
                "0.",
                LibBytes.slice(
                    abi.encodePacked(generateZeroes(decimals - len), uintToString), 0, LibUint256.min(decimals, maxIncludedDecimals)
                )
            );
        }

        return string(uintToString);
    }
}

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

import "./ctypes.sol";

/// @title Ticket Array Custom Type
library LTicketArray {
    function get(ctypes.TicketArray position) internal pure returns (ctypes.Ticket[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

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

import "./ctypes.sol";

/// @title Cask Array Custom Type
library LCaskArray {
    function get(ctypes.CaskArray position) internal pure returns (ctypes.Cask[] storage data) {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    enum ClaimStatus {
        CLAIMED,
        PARTIALLY_CLAIMED,
        SKIPPED
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @title Factory Interface
/// @author mortimr @ Kiln
/// @notice The vFactory contract is in charge of depositing validators to the consensus layer
interface IvFactory is IAdministrable, IDepositor, IFixable {
    /// @notice The provided array is empty
    error EmptyArray();

    /// @notice The provided arrays do not have matching lengths
    error InvalidArrayLengths();

    /// @notice The withdrawal attempt was made on a validator that collected no funds
    error EmptyWithdrawalRecipient();

    /// @notice The provided key concatenation is empty
    /// @param index The index of the invalid key concatenation in the calldata parameters
    error EmptyKeyPayload(uint256 index);

    /// @notice The provided validator id is invalid
    /// @param id The invalid id
    error InvalidValidatorId(uint256 id);

    /// @notice The provided key concatenation is invalid
    /// @param index The index of the invalid key concatenation in the calldata parameters
    error InvalidKeyPayload(uint256 index);

    /// @notice The provided indexes array if empty
    /// @param index The index of the invalid index array in the calldata parameters
    error EmptyIndexesArray(uint256 index);

    /// @notice The provided indexes array is unsorted
    /// @param index The index of the invalid index array in the calldata parameters
    error UnsortedIndexArray(uint256 index);

    /// @notice The withdrawal call performed on the minimal recipient reverted
    /// @param rdata The resulting error return data
    error MinimalRecipientExecutionError(bytes rdata);

    /// @notice The provided withdrawal channel is invalid
    /// @param withdrawalChannel The invalid withdrawal channel
    error InvalidWithdrawalChannel(bytes32 withdrawalChannel);

    /// @notice The provided message value in ether is invalid
    /// @param received The provided amount
    /// @param expected The expected amount
    error InvalidMessageValue(uint256 received, uint256 expected);

    /// @notice The requested validator count is too high
    /// @param requested The count of validators requested
    /// @param available The count of available validators
    error NotEnoughValidators(uint256 requested, uint256 available);

    /// @notice The provided validator index is out of bounds
    /// @param index The indexes array index in the calldata
    /// @param validatorIndex The invalid validator index
    error ValidatorIndexOutOfBounds(uint256 index, uint256 validatorIndex);

    /// @notice A funded validator removal was attempted
    /// @param index The indexes array index in the calldata
    /// @param validatorIndex The funded validator index
    error FundedValidatorRemovalAttempt(uint256 index, uint256 validatorIndex);

    /// @notice Error raised when the requested total exits on a custom channel is higher than the total funded count
    /// @param withdrawalChannel The withdrawal channel
    /// @param requestedTotal The total requested exits
    /// @param maxFundedCount The count of funded validators on the channel
    error ExitTotalTooHigh(bytes32 withdrawalChannel, uint32 requestedTotal, uint32 maxFundedCount);

    /// @notice Error raised when the requested limit on a withdrawal channel is higher than the validators count.
    /// @param withdrawalChannel The withdrawal channel
    /// @param limit The limit requested
    /// @param validatorCount The count of validators on the channel
    error LimitExceededValidatorCount(bytes32 withdrawalChannel, uint256 limit, uint256 validatorCount);

    /// @notice Emitted when the minimal recipient implementation is set
    /// @param minimalRecipientImplementation The address of the implementation
    event SetMinimalRecipientImplementation(address minimalRecipientImplementation);

    /// @notice Emitted when hatcher registry is set
    /// @param hatcherRegistry The address of the hatcher registry
    event SetHatcherRegistry(address hatcherRegistry);

    /// @notice Emitted when the operator changed
    /// @param operator The new operator address
    event ChangedOperator(address operator);

    /// @notice Emitted when the treasury changed
    /// @param treasury The new treasury address
    event ChangedTreasury(address treasury);

    /// @notice Emitted when an exit request was made
    /// @param withdrawalChannel The withdrawal channel that received the exit request
    /// @param publicKey The public key of the validator that requested the exit
    /// @param id The id of the validator that requested the exit
    event ExitValidator(bytes32 indexed withdrawalChannel, bytes publicKey, uint256 id);

    /// @notice Emitted when the owner of a validator is changed
    /// @param id The id of the validator
    /// @param owner The new owner address
    event SetValidatorOwner(uint256 indexed id, address owner);

    /// @notice Emitted when the metadata of the vFactory is changed
    /// @param name The operator name
    /// @param url The operator shared url
    /// @param iconUrl The operator icon
    event SetMetadata(string name, string url, string iconUrl);

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

    /// @notice Emitted when new keys are added to a withdrawal channel
    /// @param withdrawalChannel The withdrawal channel that received new keys
    /// @param keys The keys that were added
    event AddedValidators(bytes32 indexed withdrawalChannel, bytes keys);

    /// @notice Emitted when the staking limit has been changed for a withdrawal channel
    /// @param withdrawalChannel The withdrawal channel that had its limit updated
    /// @param limit The new staking limit of the withdrawal channel
    event UpdatedLimit(bytes32 indexed withdrawalChannel, uint256 limit);

    /// @notice Emitted when funds have been withdrawn from a validator withdrawal recipient
    /// @param id The id of the validator
    /// @param recipient The address receiving the funds
    /// @param value The value that was withdrawn
    event Withdraw(uint256 indexed id, address recipient, uint256 value);

    /// @notice Emitted when a validator extra data is changed
    /// @param id The id of the validator
    /// @param extraData The new extra data value
    event SetValidatorExtraData(uint256 indexed id, string extraData);

    /// @notice Emitted when a validator fee recipient is changed
    /// @param id The id of the validator
    /// @param feeRecipient The new fee recipient address
    event SetValidatorFeeRecipient(uint256 indexed id, address feeRecipient);

    /// @notice Emitted when keys are requested on a withdrawal channel
    /// @param withdrawalChannel The withdrawal channel where keys have been requested
    /// @param total The expect total key count of the channel
    event ValidatorRequest(bytes32 indexed withdrawalChannel, uint256 total);

    /// @notice Emitted when a channel exit request is above the funded count
    /// @param funded The count of funded validators on the channel
    /// @param requestedTotal The total requested exits
    event ExitRequestAboveFunded(uint32 funded, uint32 requestedTotal);

    /// @notice Emitted when a validator key has been removed from a withdrawal channel
    /// @param withdrawalChannel The withdrawal channel where the key has been removed
    /// @param publicKey The public key that has been removed
    /// @param validatorIndex The index of the removed validator key
    event RemovedValidator(bytes32 indexed withdrawalChannel, bytes publicKey, uint256 validatorIndex);

    /// @notice Emitted when a validator key is funded
    /// @param withdrawalChannel The withdrawal channel where the validator got funded
    /// @param depositor The address of the depositor bringing the funds for the validator
    /// @param withdrawalAddress The address of the withdrawal recipient
    /// @param publicKey The BLS Public key of the funded validator
    /// @param id The unique id of the validator
    /// @param validatorIndex The index of the funded validator in the withdrawal channel
    event FundedValidator(
        bytes32 indexed withdrawalChannel,
        address indexed depositor,
        address indexed withdrawalAddress,
        bytes publicKey,
        uint256 id,
        uint256 validatorIndex
    );

    /// @notice Emitted when the total exit for a custom withdrawal channel is changed
    /// @param withdrawalChannel The withdrawal channel where the exit count is changed
    /// @param totalExited The new total exited value
    event SetExitTotal(bytes32 indexed withdrawalChannel, uint32 totalExited);

    /// @notice Emitted when the last edit is after the snapshot (when editing the limit). The snapshot limit is staled.
    /// @param withdrawalChannel The withdrawal channel
    /// @param limit The limit requested
    event LastEditAfterSnapshot(bytes32 indexed withdrawalChannel, uint256 limit);

    /// @notice Initializes the vFactory
    /// @dev Can only be called once
    /// @param depositContract Address of the deposit contract to use
    /// @param admin Address of the contract admin
    /// @param operator_ Address of the contract operator
    /// @param treasury_ Address of the treasury
    /// @param minimalRecipientImplementation Address used by the clones as implementation for the withdrawal recipients
    /// @param hatcherRegistry Contract holding the hatcher registry
    function initialize(
        string memory name,
        address depositContract,
        address admin,
        address operator_,
        address treasury_,
        address minimalRecipientImplementation,
        address hatcherRegistry
    ) external;

    /// @notice Retrieve the current operator address
    /// @return The operator address
    function operator() external view returns (address);

    /// @notice Retrieve the current treasury address
    /// @return The treasury address
    function treasury() external view returns (address);

    /// @notice Retrieve the depositor status
    /// @param depositor Address to verify
    /// @param wc Withdrawal channel to verify
    /// @return Status of the depositor
    function depositors(address depositor, bytes32 wc) external view returns (bool);

    /// @notice Retrieve the details of a validator by its unique id
    /// @param id ID of the validator
    /// @return found True if the ID matches a validator
    /// @return funded True if the validator is funded
    /// @return wc The withdrawal channel of the validator
    /// @return index The index of the validator in the withdrawal channel
    /// @return publicKey The BLS public key of the validator
    /// @return signature The BLS signature of the validator
    /// @return owner The address owning the validator
    /// @return withdrawalRecipient The address where the withdrawal rewards will go to
    /// @return feeRecipient The address where the execution layer fees are expected to go to
    function validator(uint256 id)
        external
        view
        returns (
            bool found,
            bool funded,
            bytes32 wc,
            uint256 index,
            bytes memory publicKey,
            bytes memory signature,
            address owner,
            address withdrawalRecipient,
            address feeRecipient
        );

    /// @notice Retrieve the details of a validator by its unique id
    /// @param ids IDs of the validators
    /// @return  Public keys of the provided IDs
    function publicKeys(uint256[] calldata ids) external view returns (bytes[] memory);

    /// @notice Retrieve the details of a key in a withdrawalChannel
    /// @param wc The withdrawal channel the key is stored in
    /// @param index The index of the key in the withdrawal channel
    /// @return found True if there's a key at the given index in the withdrawal channel
    /// @return publicKey The BLS public key of the validator
    /// @return signature The BLS signature of the validator
    /// @return withdrawalRecipient The address where the withdrawal rewards will go to
    function key(bytes32 wc, uint256 index)
        external
        view
        returns (bool found, bytes memory publicKey, bytes memory signature, address withdrawalRecipient);

    /// @notice Retrieve the number of validators owned by an account in a specific withdrawal channel
    /// @param wc The withdrawal channel to inspect
    /// @param owner The account owning the validators
    /// @return The number of owned validators in the withdrawal channel
    function balance(bytes32 wc, address owner) external view returns (uint256);

    /// @notice Retrieve the key details of the withdrawal channel
    /// @param wc The withdrawal channel to inspect
    /// @return total The total count of deposited keys
    /// @return limit The staking limit of the channel
    /// @return funded The count of funded validators
    function withdrawalChannel(bytes32 wc) external view returns (uint32 total, uint32 limit, uint32 funded);

    /// @notice Retrieve the operator public metadata
    /// @return name The operator name. Cannot be empty.
    /// @return url The operator shared url. Can be empty.
    /// @return iconUrl The operator icon url
    function metadata() external view returns (string memory name, string memory url, string memory iconUrl);

    /// @notice Retrieve the withdrawal address for the specified public key
    /// @dev This is only useful on the null withdrawal channel where the vFactory spawns
    ///      minimal clones deterministically as the withdrawal recipients of each validator.
    /// @param publicKey The BLS Public Key of the validator
    /// @return The address where the minimal clone will be deployed to retrieve the consensus layer rewards
    function withdrawalAddress(bytes calldata publicKey) external view returns (address);

    /// @notice Retrieve the count of fundable validators on a withdrawal channel
    /// @param wc The withdrawal channel to inspect
    /// @return The count of fundable validators
    function availableValidators(bytes32 wc) external view returns (uint256);

    /// @notice Changes the operator address
    /// @dev Only callable by the admin
    /// @param newOperator New operator address
    function setOperator(address newOperator) external;

    /// @notice Changes the operator public metadata
    /// @param name The operator name. Cannot be empty.
    /// @param url The operator shared url. Can be empty.
    /// @param iconUrl The operator icon url
    function setMetadata(string calldata name, string calldata url, string calldata iconUrl) external;

    /// @notice Add or remove depositor
    /// @dev Callable by the admin of the factory or the nexus
    /// @param depositor The address to add or remove
    /// @param wc The withdrawal channel to add or remove the depositor from
    /// @param allowed True to allow as depositor
    function allowDepositor(address depositor, bytes32 wc, bool allowed) external;

    /// @notice Emits an event signaling a request in keys on a specific withdrawal channel
    /// @param wc The withdrawal channel to perform the request on
    /// @param amount The amount of keys that should be added to the channel
    function request(bytes32 wc, uint256 amount) external;

    /// @notice Adds keys to several withdrawal channels
    /// @dev It's expected that the provided withdrawalChannels and _keys have the same length.
    ///      For each withdrawalChannel, a concatenation of [S1,P1,S2,P2...,SN,PN] is expected.
    ///      S = BLS Signature and P = BLS Public Key. Signature should come first in each pair.
    /// @param withdrawalChannels The list of withdrawal channels to add keys on
    /// @param keys The list of key concatenations to add to the withdrawal channels
    function addValidators(bytes32[] calldata withdrawalChannels, bytes[] calldata keys) external;

    /// @notice Removes keys from several withdrawal channels
    /// @dev It's expected that the provided withdrawalChannels and _indexes have the same length.
    ///      For each withdrawalChannel, an array of indexes is expected. These indexes should be sorted in descending order.
    ///      Each array should not contain any duplicate index.
    /// @param withdrawalChannels The list of withdrawal channels to add keys on
    /// @param indexes The list of lists of indexes to remove from the withdrawal channels
    function removeValidators(bytes32[] calldata withdrawalChannels, uint256[][] calldata indexes) external;

    /// @notice Modifies the staking limits of several withdrawal channels
    /// @dev It's expected that the provided withdrawalChannels, _limits and _snapshots have the same length
    ///      For each withdrawalChannel, a new limit is provided alongside a snapshot block number.
    ///      If the new limit value decreases the current one, no extra check if performed and the limit is decreased.
    ///      If the new limit value increases the current one, we check that no key modifictions have been done after
    ///      the provided snapshot block. If it's the case, we don't update the limit and we don't revert, we simply
    ///      emit an event alerting that the last key edition happened after the snapshot. Otherwise the limit is increased.
    /// @param withdrawalChannels The list of withdrawal channels to update the limits
    /// @param limits The list of new staking limits values
    /// @param snapshots The list of block snapshots to respect if the limit is increased
    function approve(bytes32[] calldata withdrawalChannels, uint256[] calldata limits, uint256[] calldata snapshots) external;

    /// @notice Deposits _count validators on the provided withdrawal channel
    /// @dev This call reverts if the count of available keys is too low on the withdrawal channel
    /// @param wc The withdrawal channel to fund keys on
    /// @param count The amount of keys to fund
    /// @param feeRecipient The fee recipient to set all the funded keys on
    /// @param owner The address owning the validators
    /// @param extradata The extra data to transmit to the node operator
    /// @return An array of unique IDs identifying the funded validators
    function deposit(bytes32 wc, uint256 count, address feeRecipient, address owner, string calldata extradata)
        external
        payable
        returns (uint256[] memory);

    /// @notice Changes the fee recipient of several validators
    /// @dev Only callable by the owner of the validators
    /// @param ids The list of validator IDs
    /// @param newFeeRecipient The new fee recipient address
    function setFeeRecipient(uint256[] calldata ids, address newFeeRecipient) external;

    /// @notice Changes the owner of several validators
    /// @dev Only callable by the owner of the validators
    /// @param ids The list of validator IDs
    /// @param newOwner The new owner address
    function setOwner(uint256[] calldata ids, address newOwner) external;

    /// @notice Changes the extradata of several validators
    /// @dev Only callable by the owner of the validators
    /// @param ids The list of validator IDs
    /// @param newExtradata The new validator extra data
    function setExtraData(uint256[] calldata ids, string calldata newExtradata) external;

    /// @notice Emits an exit request event for several validators
    /// @dev Only callable by the owner of the validators
    /// @param ids The list of validator IDs
    function exit(uint256[] calldata ids) external;

    /// @notice Perform a consensus layer withdrawal on several validators
    /// @dev Only callable by the owner of the validators and on funded validators from the null withdrawal channel
    /// @param ids The list of validator IDs
    /// @param recipient The address that should receive the funds, that implements the WithdrawRecipientLike interface
    function withdraw(uint256[] calldata ids, address recipient) external;

    /// @notice Requests a new total exited validator count for the withdrawal recipient calling the method
    /// @dev This endpoint is callable by any address, it's up to the operator to properly filter the calls
    ///      based on existing withdrawal channels only.
    /// @param totalExited The new total exited validator count for the withdrawal channel
    /// @return The new total exited validator count for the withdrawal channel
    function exitTotal(uint32 totalExited) external returns (uint32);
}

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

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

File 14 of 41 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 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);
}

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

import "./types.sol";

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

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

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

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

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

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

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

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

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

import "./types.sol";

library LString {
    struct StringStorage {
        string value;
    }

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

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

        return ss.value;
    }

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

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

        ss.value = value;
    }
}

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

import "./types.sol";

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

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

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

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

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

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

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

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

import "./types/uint256.sol";

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

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

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

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

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

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import "openzeppelin-contracts/token/ERC721/extensions/IERC721Metadata.sol";

/// @title NFT
/// @author mortimr @ Kiln
/// @notice NFT contract using utils.sol storage format.
interface INFT is IERC721Metadata {
    /// @notice Emitted when name is changed.
    /// @param name The new ERC721 contract name
    event SetName(string name);

    /// @notice Emitted when symbol is changed.
    /// @param symbol The new ERC721 contract symbol
    event SetSymbol(string symbol);

    /// @notice Thrown when the token is already minted when it shouldn't.
    /// @param tokenId The id of the already existing token
    error TokenAlreadyMinted(uint256 tokenId);

    /// @notice Thrown when a mint operation to address zero is attempted.
    error IllegalMintToZero();

    /// @notice Thrown when a transfer operation to address zero is attempted.
    error IllegalTransferToZero();

    /// @notice Thrown when approval to self is made.
    /// @param owner Address attempting approval to self
    error ApprovalToOwner(address owner);

    /// @notice Thrown when provided token id is invalid.
    /// @param tokenId The invalid token id
    error InvalidTokenId(uint256 tokenId);

    /// @notice Thrown when the receiving contract is not able to receive the token.
    /// @param from The address sending the token
    /// @param to The address (contract) receiving the token and failing to properly receive it
    /// @param tokenId The token id
    /// @param data The extra data provided to the call
    error NonERC721ReceiverTransfer(address from, address to, uint256 tokenId, bytes data);

    /// @notice Throw when an nft transfer was attempted while the nft is frozen.
    ///         NFTs get frozen for ever once the exit request is made.
    ///         NFTs get frozen for 6 hours when a withdrawal is made.
    /// @param tokenId The frozen token id
    /// @param currentTimestamp The timestamp where the transfer was attempted
    /// @param freezeTimestamp The timestamp until which the token is frozen
    error IllegalTransferWhileFrozen(uint256 tokenId, uint256 currentTimestamp, uint256 freezeTimestamp);

    /// @notice Retrieve the total count of validator created with this contract.
    /// @return The total count of NFT validators of this contract
    function totalSupply() external view returns (uint256);
}

//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import "./uctypes.sol";

/// @title Operator Approvals Custom Type
library LOperatorApprovalsMapping {
    function get(uctypes.OperatorApprovalsMapping position)
        internal
        pure
        returns (mapping(address => mapping(address => bool)) storage data)
    {
        // slither-disable-next-line assembly
        assembly {
            data.slot := position
        }
    }
}

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

import "./types.sol";

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

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

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

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

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

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

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

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

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

import "prb-math/PRBMath.sol";

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

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

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

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

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

/// @title Lib Bytes
/// @dev This library helps manipulating bytes.
library LibBytes {
    /// @dev The length overflows an uint.
    error SliceOverflow();

    /// @dev The slice is outside of the initial bytes bounds.
    error SliceOutOfBounds();

    /// @dev Slices the provided bytes.
    /// @param bytes_ Bytes to slice
    /// @param start The starting index of the slice
    /// @param length The length of the slice
    /// @return The slice of _bytes starting at _start of length _length
    // slither-disable-next-line dead-code
    function slice(bytes memory bytes_, uint256 start, uint256 length) internal pure returns (bytes memory) {
        unchecked {
            if (length + 31 < length) {
                revert SliceOverflow();
            }
        }
        if (bytes_.length < start + length) {
            revert SliceOutOfBounds();
        }

        bytes memory tempBytes;

        // slither-disable-next-line assembly
        assembly {
            switch iszero(length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(bytes_, lengthmod), mul(0x20, iszero(lengthmod))), start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @title Depositor Interface
/// @author mortimr @ Kiln
/// @dev Unstructured Storage Friendly
/// @notice The Depositor contract adds deposit capabilities to easily fund
///         validators and activate them on the Consensus Layer.
interface IDepositor {
    /// @notice The provided public key is not 48 bytes long.
    error InvalidPublicKeyLength();

    /// @notice The provided signature is not 96 bytes long.
    error InvalidSignatureLength();

    /// @notice The balance is too low for the deposit.
    error InvalidDepositSize();

    /// @notice An error occured during the deposit.
    error DepositError();

    /// @notice The deposit contract address has been updated.
    /// @param depositContract The new deposit contract address
    event SetDepositContract(address depositContract);

    /// @notice Retrieve the deposit contract address.
    /// @return depositContractAddress The deposit contract address
    function depositContract() external view returns (address depositContractAddress);
}

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 34 of 41 : uctypes.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

/// @title Uitls Custom Types
// slither-disable-next-line naming-convention
library uctypes {
    type OperatorApprovalsMapping is bytes32;
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

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

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

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

    /// FUNCTIONS ///

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

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

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

    struct PublicKey {
        bytes32 A;
        bytes16 B;
    }

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"currentVersion","type":"uint256"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ApprovalToOwner","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"rdata","type":"bytes"}],"name":"ClaimTransferFailed","type":"error"},{"inputs":[],"name":"IllegalMintToZero","type":"error"},{"inputs":[],"name":"IllegalTransferToZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"internalType":"uint256","name":"freezeTimestamp","type":"uint256"}],"name":"IllegalTransferWhileFrozen","type":"error"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"InvalidCaskId","type":"error"},{"inputs":[],"name":"InvalidEmptyString","type":"error"},{"inputs":[],"name":"InvalidLengths","type":"error"},{"inputs":[],"name":"InvalidNullValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"InvalidTicketId","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"InvalidTokenId","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"NonERC721ReceiverTransfer","type":"error"},{"inputs":[{"internalType":"uint256","name":"prod1","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath__MulDivOverflow","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SliceOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"ticketId","type":"uint256"},{"internalType":"uint32","name":"caskId","type":"uint32"}],"name":"TicketNotMatchingCask","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"TransferDisabled","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"ticketId","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"caskId","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"amountFilled","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amountEthFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unclaimedEth","type":"uint256"}],"name":"FilledTicket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Payment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint32","name":"idx","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint128","name":"position","type":"uint128"},{"internalType":"uint128","name":"size","type":"uint128"},{"internalType":"uint128","name":"maxExitable","type":"uint128"}],"indexed":false,"internalType":"struct ctypes.Ticket","name":"ticket","type":"tuple"}],"name":"PrintedTicket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"components":[{"internalType":"uint128","name":"position","type":"uint128"},{"internalType":"uint128","name":"size","type":"uint128"},{"internalType":"uint128","name":"value","type":"uint128"}],"indexed":false,"internalType":"struct ctypes.Cask","name":"cask","type":"tuple"}],"name":"ReceivedCask","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"SetName","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"SetPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"SetSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"tokenUriImageUrl","type":"string"}],"name":"SetTokenUriImageUrl","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SetTransferEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"unclaimedFunds","type":"uint256"}],"name":"SetUnclaimedFunds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SuppliedEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"oldTicketId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newTicketId","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"ticketIndex","type":"uint32"}],"name":"TicketIdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"cask","outputs":[{"components":[{"internalType":"uint128","name":"position","type":"uint128"},{"internalType":"uint128","name":"size","type":"uint128"},{"internalType":"uint128","name":"value","type":"uint128"}],"internalType":"struct ctypes.Cask","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"caskCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ticketIds","type":"uint256[]"},{"internalType":"uint32[]","name":"caskIds","type":"uint32[]"},{"internalType":"uint16","name":"maxClaimDepth","type":"uint16"}],"name":"claim","outputs":[{"internalType":"enum IvExitQueue.ClaimStatus[]","name":"statuses","type":"uint8[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"feed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fix","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vpool","type":"address"},{"internalType":"string","name":"newTokenUriImageUrl","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onvPoolSharesReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"max","type":"uint256"}],"name":"pull","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ticketIds","type":"uint256[]"}],"name":"resolve","outputs":[{"internalType":"int64[]","name":"caskIdsOrErrors","type":"int64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newTokenUriImageUrl","type":"string"}],"name":"setTokenUriImageUrl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setTransferEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ticket","outputs":[{"components":[{"internalType":"uint128","name":"position","type":"uint128"},{"internalType":"uint128","name":"size","type":"uint128"},{"internalType":"uint128","name":"maxExitable","type":"uint128"}],"internalType":"struct ctypes.Ticket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ticketCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"idx","type":"uint32"}],"name":"ticketIdAtIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenUriImageUrl","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unclaimedFunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b50620000516000197fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7660001b6200005760201b62001ef31790919060201c565b6200005b565b9055565b614ea7806200006b6000396000f3fe6080604052600436106101d85760003560e01c806395d89b4111610102578063c87b56dd11610095578063f399e22e11610064578063f399e22e14610570578063f59dfdfb14610590578063f8c21535146105a3578063fe34b92f146105d057600080fd5b8063c87b56dd146104fb578063cfbd900f1461051b578063e985e9c514610530578063eddd1bed1461055057600080fd5b8063a551878e116100d1578063a551878e14610482578063adcf11631461048e578063b88d4fde146104bb578063c129ff32146104db57600080fd5b806395d89b41146103dd5780639b21cf6c146104095780639fe9f62314610442578063a22cb4651461046257600080fd5b806342842e0e1161017a5780636352211e116101495780636352211e1461035057806370a0823114610370578063713236d51461039057806382fef84a146103b057600080fd5b806342842e0e146102e65780634cd412d5146103065780634d0392a81461031b57806353a1a2031461033b57600080fd5b8063095ea7b3116101b6578063095ea7b31461026c57806318160ddd1461028e57806323b872dd146102b157806341a52b98146102d157600080fd5b806301ffc9a7146101dd57806306fdde0314610212578063081812fc14610234575b600080fd5b3480156101e957600080fd5b506101fd6101f8366004613e1d565b6105e5565b60405190151581526020015b60405180910390f35b34801561021e57600080fd5b5061022761061c565b6040516102099190613e8a565b34801561024057600080fd5b5061025461024f366004613e9d565b610718565b6040516001600160a01b039091168152602001610209565b34801561027857600080fd5b5061028c610287366004613ecb565b610749565b005b34801561029a57600080fd5b506102a36107f7565b604051908152602001610209565b3480156102bd57600080fd5b5061028c6102cc366004613ef7565b610852565b3480156102dd57600080fd5b506102276108a1565b3480156102f257600080fd5b5061028c610301366004613ef7565b6108ba565b34801561031257600080fd5b506101fd6108d5565b34801561032757600080fd5b5061028c610336366004613e9d565b6108ff565b34801561034757600080fd5b506102a3610a0e565b34801561035c57600080fd5b5061025461036b366004613e9d565b610a26565b34801561037c57600080fd5b506102a361038b366004613f38565b610a5e565b34801561039c57600080fd5b5061028c6103ab366004613f9d565b610aa2565b3480156103bc57600080fd5b506103d06103cb366004613fde565b610bb9565b6040516102099190614004565b3480156103e957600080fd5b5060408051808201909152600381526276455160e81b6020820152610227565b34801561041557600080fd5b506104296104243660046140a1565b610c7e565b6040516001600160e01b03199091168152602001610209565b34801561044e57600080fd5b5061028c61045d366004614164565b610d0b565b34801561046e57600080fd5b5061028c61047d36600461417f565b610e20565b34801561028c57600080fd5b34801561049a57600080fd5b506104ae6104a93660046141f8565b610e2b565b6040516102099190614298565b3480156104c757600080fd5b5061028c6104d63660046140a1565b61160a565b3480156104e757600080fd5b506103d06104f6366004613e9d565b611634565b34801561050757600080fd5b50610227610516366004613e9d565b6116f0565b34801561052757600080fd5b506102a3611a5b565b34801561053c57600080fd5b506101fd61054b3660046142fa565b611a71565b34801561055c57600080fd5b506102a361056b366004613fde565b611abe565b34801561057c57600080fd5b5061028c61058b366004614333565b611af0565b61028c61059e366004613e9d565b611c44565b3480156105af57600080fd5b506105c36105be366004614387565b611e0b565b604051610209919061442c565b3480156105dc57600080fd5b506102a3611edb565b60006001600160e01b031982166380ac58cd60e01b148061061657506001600160e01b03198216635b5e139f60e01b145b92915050565b60606000610628611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190614473565b6001600160a01b031663392f37e96040518163ffffffff1660e01b8152600401600060405180830381865afa1580156106c6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106ee91908101906144d5565b5050905080604051602001610703919061455c565b60405160208183030381529060405291505090565b600061072382611f0f565b610616600080516020614d328339815191525b6000848152602091909152604090205490565b600061075482611f38565b9050806001600160a01b0316836001600160a01b0316036107985760405163b8c748d160e01b81526001600160a01b03821660048201526024015b60405180910390fd5b336001600160a01b038216148015906107b857506107b68133611a71565b155b156107e75760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b6107f2838284611f51565b505050565b60006108217f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc25490565b7f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a15461084d91906145a1565b905090565b61085c3382611fbf565b610896573361086a82611f38565b60405163295a81c160e01b81526001600160a01b0392831660048201529116602482015260440161078f565b6107f283838361201d565b606061084d600080516020614e128339815191526121d6565b6107f28383836040518060200160405280600081525061160a565b600061084d7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a05490565b600080516020614df2833981519152546001600160a01b0316336001600160a01b03161461093e573361086a600080516020614df28339815191525490565b6000610956600080516020614d928339815191525490565b9050818118818311028083189083146107f25761097b61097682846145a1565b61226e565b6040518181527f1cd073ba6c9a2801c4cf3b0aa11dde3dcbe68c2f5d750d00e09e884a4d83d4a79060200160405180910390a16109b6611ef7565b6001600160a01b0316636a3a2119826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156109f057600080fd5b505af1158015610a04573d6000803e3d6000fd5b5050505050505050565b6000600080516020614e328339815191525b54919050565b600080610a3283611f38565b90506001600160a01b0381166106165760405163ed15e6cf60e01b81526004810184905260240161078f565b6000610a69826122bc565b600080516020614d728339815191526000610a8c846001600160a01b03166122e3565b8152602001908152602001600020549050919050565b6000610aac611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0d9190614473565b6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190614473565b9050336001600160a01b03821614610baa5760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b50610bb582826122f4565b5050565b6040805160608101825260008082526020820181905291810191909152600080516020614e32833981519152805463ffffffff841610610c1457604051631cb7377f60e11b815263ffffffff8416600482015260240161078f565b808363ffffffff1681548110610c2c57610c2c6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091529392505050565b6000610c96600080516020614df28339815191525490565b6001600160a01b0316336001600160a01b031614610cc5573361086a600080516020614df28339815191525490565b610cce836123d4565b8151601403610cef57602082015160601c610ce984826123f5565b50610cf9565b610cf983856123f5565b506326c873db60e21b5b949350505050565b6000610d15611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d769190614473565b6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd79190614473565b9050336001600160a01b03821614610e135760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b50610e1d816126a6565b50565b610bb5338383612701565b6060610e35613d41565b858152851580610e46575080518414155b15610e6457604051631df89e8b60e01b815260040160405180910390fd5b600080516020614e52833981519152546020820152600080516020614e328339815191525460a0808301510152856001600160401b03811115610ea957610ea9614034565b604051908082528060200260200182016040528015610ed2578160200160208202803683370190505b509150856001600160401b03811115610eed57610eed614034565b604051908082528060200260200182016040528015610f16578160200160208202803683370190505b506040820152856001600160401b03811115610f3457610f34614034565b604051908082528060200260200182016040528015610f5d578160200160208202803683370190505b5060608201526000608082018190525b81518110156114df57878782818110610f8857610f886145b4565b60a0850180516020929092029390930135905250515160801c60a083015160200152858582818110610fbc57610fbc6145b4565b9050602002016020810190610fd19190613fde565b60a08301805163ffffffff909216606090920191909152805161ffff861660c09091015251600060e0909101819052611021898984818110611015576110156145b4565b90506020020135610a26565b9050600080516020614e528339815191528360a00151602001518154811061104b5761104b6145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b90910481168585015260019091015481168483015260a0880180518301949094529251015101511690036110f55760028483815181106110bd576110bd6145b4565b602002602001019060028111156110d6576110d6614282565b908160028111156110e9576110e9614282565b90525050600101610f6d565b8260a0015160a001518360a001516060015163ffffffff161061113d5760a083015160600151604051631cb7377f60e11b815263ffffffff909116600482015260240161078f565b600080516020614e328339815191528360a001516060015163ffffffff168154811061116b5761116b6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b909104811694840194909452600101549092168183015260a08501805160809081019290925251918201519101516111d491906127cb565b61120d5760a08301518051606090910151604051633e346b6760e01b8152600481019290925263ffffffff16602482015260440161078f565b61121a8360a00151612818565b60a083015160400151602001516001600160801b03161561134f5760006112598a8a8581811061124c5761124c6145b4565b9050602002013560801c90565b905061127c8a8a85818110611270576112706145b4565b90506020020135612bd8565b60006112f382600080516020614e5283398151915284815481106112a2576112a26145b4565b60009182526020918290206040805160608101825260029390930290910180546001600160801b038082168552600160801b90910481169484018590526001909101541691015260809190911b1790565b90506112ff8382612d05565b8163ffffffff16818c8c87818110611319576113196145b4565b905060200201357f08a4aef48af435497d1effe4f4b06688b8955e5db1b5b37af57c7f18717ede8960405160405180910390a450505b60008360a0015160400151602001516001600160801b031611611373576000611376565b60015b848381518110611388576113886145b4565b602002602001019060028111156113a1576113a1614282565b908160028111156113b4576113b4614282565b90525060a083015160e00151156114d65760001960005b846080015181101561141d57826001600160a01b0316856040015182815181106113f7576113f76145b4565b60200260200101516001600160a01b0316036114155780915061141d565b6001016113cb565b50801961149d5781846040015185608001518151811061143f5761143f6145b4565b60200260200101906001600160a01b031690816001600160a01b0316815250508360a0015160e001518460600151856080015181518110611482576114826145b4565b602090810291909101015260808401805160010190526114d4565b8360a0015160e00151846060015182815181106114bc576114bc6145b4565b602002602001018181516114d091906145ca565b9052505b505b50600101610f6d565b5060005b81608001518110156115ff57600082604001518281518110611507576115076145b4565b60200260200101519050600083606001518381518110611529576115296145b4565b60200260200101519050600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114611581576040519150601f19603f3d011682016040523d82523d6000602084013e611586565b606091505b5091509150816115ad5783816040516329a5fdaf60e21b815260040161078f9291906145dd565b836001600160a01b03167fd4f43975feb89f48dd30cabbb32011045be187d1e11c8ea9faa43efc35282519846040516115e891815260200190565b60405180910390a2846001019450505050506114e3565b505095945050505050565b6116143383611fbf565b611622573361086a83611f38565b61162e84848484612e77565b50505050565b6040805160608101825260008082526020820181905291810191909152600080516020614e528339815191528054608084901c9190821061168b576040516351d7ea4f60e01b81526004810185905260240161078f565b80828154811061169d5761169d6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b9091048116948401949094526001015490921691810191909152949350505050565b60606116fb82611f0f565b60006117078360801c90565b90506000600080516020614e52833981519152828154811061172b5761172b6145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b90910481169385019390935260010154909116908201529150611789600080516020614e3283398151915290565b90506000806000808480549050116117bd576040805160608101825260008082526020820181905291810191909152611829565b835484906117cd906001906145a1565b815481106117dd576117dd6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b6020860151865191925061183c91614601565b6001600160801b031681600001516001600160801b0316111561186e5784602001516001600160801b03169250611985565b845181516001600160801b0391821691161061191457602085015185516118959190614601565b6001600160801b0316816020015182600001516118b29190614601565b6001600160801b031610156118f257602081015181516118d29190614601565b602086015186516118e39190614601565b6118ed9190614621565b6118f5565b60005b85602001516119049190614621565b6001600160801b03169250611985565b845181516001600160801b03918216911610801561195757506020810151815161193e9190614601565b6001600160801b031685600001516001600160801b0316105b156119855784516020820151825161196f9190614601565b6119799190614621565b6001600160801b031692505b602081015181516119969190614601565b6001600160801b03169150600090506119bc600080516020614e128339815191526121d6565b6119c530612eb3565b6119ce8a612ec9565b6040516020016119e093929190614641565b6040516020818303038152906040529050611a2f6119fd87612ec9565b611a08878686612f5b565b83604051602001611a1b9392919061469b565b604051602081830303815290604052612fe1565b604051602001611a3f91906147d2565b6040516020818303038152906040529650505050505050919050565b6000600080516020614e52833981519152610a20565b6001600160a01b0391821660009081527f6c716a91f6b5f5a0aa2affaf44bd88ea94ec69e363cf1fe9251e00a0fcc6c34e6020908152604080832093909416825291909152205460ff1690565b600061061663ffffffff8316600080516020614e528339815191528463ffffffff16815481106112a2576112a26145b4565b6000611b1a7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b8103611bf957611b52611b2e8260016145ca565b7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7655565b7f91efa3d50feccde0d0d202f8ae5c41ca0b2be614cebcb2bd2f4b019396e6568a81600036604051611b8693929190614817565b60405180910390a1611b9883836122f4565b611ba1846122bc565b611bb8600080516020614df2833981519152859055565b6040516001600160a01b03851681527f67816c9262630d6052ccaada1732fda377aa9e9abb3bc91cfd887a016a18a43a9060200160405180910390a161162e565b80611c227fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b60405163031b997760e51b81526004810192909252602482015260440161078f565b600080516020614df2833981519152546001600160a01b0316336001600160a01b031614611c83573361086a600080516020614df28339815191525490565b611c8c816123d4565b611c95346123d4565b600080516020614e328339815191528054600081611ccf576040805160608101825260008082526020820181905291810191909152611d37565b82611cdb6001846145a1565b81548110611ceb57611ceb6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b90506000604051806060016040528083602001518460000151611d5a9190614601565b6001600160801b039081168252878116602080840191909152348216604093840152875460018181018a5560008a81528390208651938701518516600160801b029385169390931760029092029092019081558484015191018054919092166001600160801b031991909116179055519091507f7833b416621d0b35128379673a1aa96c979020b58203e7b60c5c93f4d52a7f6c90611dfc908590849061484d565b60405180910390a15050505050565b8051606090806001600160401b03811115611e2857611e28614034565b604051908082528060200260200182016040528015611e51578160200160208202803683370190505b50600080516020614e5283398151915254600080516020614e32833981519152549193509060005b83811015611ed257611ea5868281518110611e9657611e966145b4565b60200260200101518484613133565b858281518110611eb757611eb76145b4565b60079290920b60209283029190910190910152600101611e79565b50505050919050565b600061084d600080516020614d928339815191525490565b9055565b600061084d600080516020614df28339815191525490565b611f18816132b3565b610e1d5760405163ed15e6cf60e01b81526004810182905260240161078f565b6000610616600080516020614d52833981519152610736565b611f63836001600160a01b03166122e3565b6000828152600080516020614d32833981519152602052604080822092909255905182916001600160a01b0386811692908616917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b600080611fcb83611f38565b9050806001600160a01b0316846001600160a01b03161480611ff25750611ff28185611a71565b80610d035750836001600160a01b031661200b84610718565b6001600160a01b031614949350505050565b6001600160a01b0382166120445760405163304bef0b60e01b815260040160405180910390fd5b826001600160a01b031661205782611f38565b6001600160a01b03161461209b5761206e81611f38565b60405163295a81c160e01b81526001600160a01b039182166004820152908416602482015260440161078f565b6120a68383836132d0565b826001600160a01b03166120b982611f38565b6001600160a01b0316146120d05761206e81611f38565b600080516020614d32833981519152600082815260209190915260408120556001600080516020614d728339815191526000612114866001600160a01b03166122e3565b8152602081019190915260400160002080549190910390556001600080516020614d728339815191526000612151856001600160a01b03166122e3565b815260208101919091526040016000208054909101905561217a6001600160a01b0383166122e3565b6000828152600080516020614d52833981519152602052604080822092909255905182916001600160a01b0385811692908716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8054606090829081906121e890614889565b80601f016020809104026020016040519081016040528092919081815260200182805461221490614889565b80156122615780601f1061223657610100808354040283529160200191612261565b820191906000526020600020905b81548152906001019060200180831161224457829003601f168201915b5050505050915050919050565b612285600080516020614d92833981519152829055565b6040518181527ffc7d134b2e716a81746c1abdbababc8c42ec12a09a1ed70f07f27bdb3646e66d906020015b60405180910390a150565b6001600160a01b038116610e1d5760405163f6b2911f60e01b815260040160405180910390fd5b60006001600160a01b038216610616565b61233382828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061330f92505050565b61238382828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600080516020614e12833981519152939250506133319050565b7fdffd2b831b1643e102b26bc49c317a42cd527eb1e6fd09ced85ed26ca8aaaec36123bb600080516020614e128339815191526121d6565b6040516123c89190613e8a565b60405180910390a15050565b80600003610e1d5760405163095e705160e11b815260040160405180910390fd5b60006123ff611ef7565b90506000816001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612441573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246591906148c3565b90506000826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124cb91906148c3565b600080516020614e52833981519152805491925090600081612509576040805160608101825260008082526020820181905291810191909152612571565b826125156001846145a1565b81548110612525576125256145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b905060006040518060600160405280836020015184600001516125949190614601565b6001600160801b031681526020018a6001600160801b031681526020016125c58b6001600160801b0316898961333d565b6001600160801b03169052905060006125f08483602001516001600160801b031660809190911b1790565b85546001808201885560008881526020908190208651918701516001600160801b03928316600160801b918416919091021760029094020192835560408601519290910180546001600160801b0319169290911691909117905590506126568982612d05565b886001600160a01b03167e80df45f12186856da484a1494bb51907e2abec5abc9a401e443c116bed71a5858385604051612692939291906148dc565b60405180910390a250505050505050505050565b6126cf7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a0829055565b60405181151581527f5bff65e3b00078a5ca87389c773003039a08945fe60195f722c109a37c4cca4f906020016122b1565b816001600160a01b0316836001600160a01b03160361273e5760405163b8c748d160e01b81526001600160a01b038416600482015260240161078f565b6001600160a01b0383811660008181527f6c716a91f6b5f5a0aa2affaf44bd88ea94ec69e363cf1fe9251e00a0fcc6c34e6020908152604080832094871680845294825291829020805460ff1916861515908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050565b602081015181516000916127de91614601565b6001600160801b031683600001516001600160801b03161080156128115750815183516001600160801b03918216911610155b9392505050565b60808101516020810151905160009161283091614601565b6040830151602081015190516001600160801b0392831693506000926128699281169161285e9116856145a1565b808218908211021890565b905060006128a3826001600160801b03168560800151604001516001600160801b03168660800151602001516001600160801b031661333d565b905060006128dd836001600160801b03168660400151604001516001600160801b03168760400151602001516001600160801b031661333d565b90506000826001600160801b0316826001600160801b0316101561293a576129058284614621565b6001600160801b0316905081925061293a81612930600080516020614d9283398151915260001b5490565b61097691906145ca565b8386604001516000018181516129509190614601565b6001600160801b031690525060408601516020018051859190612974908390614621565b6001600160801b0316905250604080870151018051849190612997908390614621565b6001600160801b0390811690915260e0880180519186169250906129bc9083906145ca565b9052506060868101518751604080516001600160801b03808a1682528816602082015290810185905263ffffffff9092169290917fef3abb819e31c9009302363931f93286b338da7cab0c25e6f948c7955fd5fd44910160405180910390a360008660400151602001516001600160801b0316118015612a54575060a08601516060870151612a4c90600161491f565b63ffffffff16105b8015612a68575060008660c0015161ffff16115b15612b2857600186606001818151612a80919061491f565b63ffffffff16905250600080516020614e32833981519152866060015163ffffffff1681548110612ab357612ab36145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260019081015490931690820152608088015260c087018051612b1390839061493c565b61ffff16905250612b2386612818565b612bd0565b8560400151602001516001600160801b0316600003612b4c578551612b4c90612bd8565b6000600080516020614e528339815191529050866040015181886020015181548110612b7a57612b7a6145b4565b60009182526020918290208351928401516001600160801b03938416600160801b918516919091021760029290920201908155604090920151600190920180546001600160801b03191692909116919091179055505b505050505050565b612be181611f0f565b612bea81611f0f565b6000612c03600080516020614d52833981519152610736565b90506001600080516020614d728339815191526000612c2a846001600160a01b03166122e3565b815260208101919091526040016000208054919091039055612c94612c6d7f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc25490565b6001017f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc255565b6000828152600080516020614d3283398151915260209081526040808320839055600080516020614d52833981519152909152808220829055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6001600160a01b038216612d2b5760405162f5345360e01b815260040160405180910390fd5b612d34816132b3565b15612d55576040516322d1d39560e21b81526004810182905260240161078f565b612d5e816132b3565b15612d7f576040516322d1d39560e21b81526004810182905260240161078f565b6001600080516020614d728339815191526000612da4856001600160a01b03166122e3565b8152602081019190915260400160002080549091019055612e0d612de67f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a15490565b6001017f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a155565b612e1f826001600160a01b03166122e3565b6000828152600080516020614d52833981519152602052604080822092909255905182916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b612e8284848461201d565b612e8e8484848461334a565b61162e5783838383604051630eefeeed60e21b815260040161078f9493929190614957565b60606106166001600160a01b0383166014613451565b60606000612ed6836135ec565b60010190506000816001600160401b03811115612ef557612ef5614034565b6040519080825280601f01601f191660200182016040528015612f1f576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084612f2957509392505050565b6060612f7784600001516001600160801b0316601260036136c4565b612f8483601260036136c4565b612f9185601260036136c4565b612fab87602001516001600160801b0316601260036136c4565b612fb5888861385a565b604051602001612fc99594939291906149e1565b60405160208183030381529060405290509392505050565b6060815160000361300057505060408051602081019091526000815290565b6000604051806060016040528060408152602001614db2604091399050600060038451600261302f91906145ca565b6130399190614b2a565b613044906004614b4c565b6001600160401b0381111561305b5761305b614034565b6040519080825280601f01601f191660200182016040528015613085576020820181803683370190505b509050600182016020820185865187015b808210156130f1576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250613096565b505060038651066001811461310d576002811461312057613128565b603d6001830353603d6002830353613128565b603d60018303535b509195945050505050565b6000806131408560801c90565b905083811061315457600019915050612811565b6000600080516020614e528339815191528281548110613176576131766145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b9091048116938501849052600190910154169083015290925090036131d15760011992505050612811565b831580613289575080516001600160801b0316600080516020614e328339815191526131fe6001876145a1565b8154811061320e5761320e6145b4565b6000918252602090912060029091020154600160801b90046001600160801b0316600080516020614e328339815191526132496001886145a1565b81548110613259576132596145b4565b600091825260209091206002909102015461327d91906001600160801b0316614601565b6001600160801b031611155b1561329a5760021992505050612811565b6132a382613904565b63ffffffff169695505050505050565b6000806132bf83611f38565b6001600160a01b0316141592915050565b7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a0546107f25760405163a24e573d60e01b815260040160405180910390fd5b8051600003610e1d57604051638d46fe0560e01b815260040160405180910390fd5b818061162e8382614ba9565b6000610d03848484613b21565b60006001600160a01b0384163b1561344957604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061338e903390899088908890600401614957565b6020604051808303816000875af19250505080156133c9575060408051601f3d908101601f191682019092526133c691810190614c68565b60015b61342f573d8080156133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5080516000036134275785858585604051630eefeeed60e21b815260040161078f9493929190614957565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610d03565b506001610d03565b60606000613460836002614b4c565b61346b9060026145ca565b6001600160401b0381111561348257613482614034565b6040519080825280601f01601f1916602001820160405280156134ac576020820181803683370190505b509050600360fc1b816000815181106134c7576134c76145b4565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106134f6576134f66145b4565b60200101906001600160f81b031916908160001a905350600061351a846002614b4c565b6135259060016145ca565b90505b600181111561359d576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613559576135596145b4565b1a60f81b82828151811061356f5761356f6145b4565b60200101906001600160f81b031916908160001a90535060049490941c9361359681614c85565b9050613528565b5083156128115760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161078f565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061362b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310613657576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061367557662386f26fc10000830492506010015b6305f5e100831061368d576305f5e100830492506008015b61271083106136a157612710830492506004015b606483106136b3576064830492506002015b600a83106106165760010192915050565b6060836000036136ec57506040805180820190915260018152600360fc1b6020820152612811565b60006136f785612ec9565b90508360ff1660000361370b579050612811565b805160ff85168111156137a5578360ff166000036137445761373b82600061373660ff8916856145a1565b613bee565b92505050612811565b61375782600061373660ff8916856145a1565b61377e8361376860ff8916856145a1565b6137368960ff168960ff16808218908211021890565b60405160200161378f929190614c9c565b6040516020818303038152906040529150613851565b6137b28160ff87166145a1565b8460ff16116137de57604051806040016040528060018152602001600360fc1b81525092505050612811565b61382f6137f66137f18360ff89166145a1565b613cb0565b83604051602001613808929190614cd8565b60405160208183030381529060405260006137368860ff168860ff16808218908211021890565b60405160200161383f9190614d07565b60405160208183030381529060405291505b50949350505050565b60608160000361389257506040805180820190915260118152704e6f742079657420636c61696d61626c6560781b6020820152610616565b82602001516001600160801b03168210156138d757506040805180820190915260138152725061727469616c6c7920636c61696d61626c6560681b6020820152610616565b5060408051808201909152600f81526e46756c6c7920636c61696d61626c6560881b602082015292915050565b600080516020614e328339815191528054600091908290613927906001906145a1565b90506000600080516020614e52833981519152858154811061394b5761394b6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091528354909150613a09908290859063ffffffff86169081106139b9576139b96145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091526127cb565b15613a1657509392505050565b6000613a3482858363ffffffff16815481106139b9576139b96145b4565b15613a425795945050505050565b8263ffffffff168163ffffffff1614613b185760006001613a63858461491f565b63ffffffff16901c90506000858263ffffffff1681548110613a8757613a876145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091529050613ade84826127cb565b15613aee57509695505050505050565b805184516001600160801b0391821691161015613b0d57819450613b11565b8192505b5050613a42565b95945050505050565b6000808060001985870985870292508281108382030391505080600003613b5b57838281613b5157613b51614994565b0492505050612811565b838110613b8557604051631dcf306360e21b8152600481018290526024810185905260440161078f565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60608182601f011015613c14576040516323d5783d60e11b815260040160405180910390fd5b613c1e82846145ca565b84511015613c3f57604051633b99b53d60e01b815260040160405180910390fd5b606082158015613c5e5760405191506000825260208201604052613851565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613c97578051835260209283019201613c7f565b5050858452601f01601f19166040525050949350505050565b60606000826001600160401b03811115613ccc57613ccc614034565b6040519080825280601f01601f191660200182016040528015613cf6576020820181803683370190505b50905060005b83811015613d3a57600360fc1b828281518110613d1b57613d1b6145b4565b60200101906001600160f81b031916908160001a905350600101613cfc565b5092915050565b6040518060c001604052806000815260200160008152602001606081526020016060815260200160008152602001613d77613d7c565b905290565b6040518061010001604052806000815260200160008152602001613db9604080516060810182526000808252602082018190529181019190915290565b815260006020820152604001613de8604080516060810182526000808252602082018190529181019190915290565b815260200160008152602001600061ffff168152602001600081525090565b6001600160e01b031981168114610e1d57600080fd5b600060208284031215613e2f57600080fd5b813561281181613e07565b60005b83811015613e55578181015183820152602001613e3d565b50506000910152565b60008151808452613e76816020860160208601613e3a565b601f01601f19169290920160200192915050565b6020815260006128116020830184613e5e565b600060208284031215613eaf57600080fd5b5035919050565b6001600160a01b0381168114610e1d57600080fd5b60008060408385031215613ede57600080fd5b8235613ee981613eb6565b946020939093013593505050565b600080600060608486031215613f0c57600080fd5b8335613f1781613eb6565b92506020840135613f2781613eb6565b929592945050506040919091013590565b600060208284031215613f4a57600080fd5b813561281181613eb6565b60008083601f840112613f6757600080fd5b5081356001600160401b03811115613f7e57600080fd5b602083019150836020828501011115613f9657600080fd5b9250929050565b60008060208385031215613fb057600080fd5b82356001600160401b03811115613fc657600080fd5b613fd285828601613f55565b90969095509350505050565b600060208284031215613ff057600080fd5b813563ffffffff8116811461281157600080fd5b60608101610616828480516001600160801b03908116835260208083015182169084015260409182015116910152565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561407257614072614034565b604052919050565b60006001600160401b0382111561409357614093614034565b50601f01601f191660200190565b600080600080608085870312156140b757600080fd5b84356140c281613eb6565b935060208501356140d281613eb6565b92506040850135915060608501356001600160401b038111156140f457600080fd5b8501601f8101871361410557600080fd5b80356141186141138261407a565b61404a565b81815288602083850101111561412d57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b8035801515811461415f57600080fd5b919050565b60006020828403121561417657600080fd5b6128118261414f565b6000806040838503121561419257600080fd5b823561419d81613eb6565b91506141ab6020840161414f565b90509250929050565b60008083601f8401126141c657600080fd5b5081356001600160401b038111156141dd57600080fd5b6020830191508360208260051b8501011115613f9657600080fd5b60008060008060006060868803121561421057600080fd5b85356001600160401b038082111561422757600080fd5b61423389838a016141b4565b9097509550602088013591508082111561424c57600080fd5b50614259888289016141b4565b909450925050604086013561ffff8116811461427457600080fd5b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000919084820190604085019084805b828110156142ed578451600381106142db57634e487b7160e01b83526021600452602483fd5b845293850193928501926001016142b5565b5091979650505050505050565b6000806040838503121561430d57600080fd5b823561431881613eb6565b9150602083013561432881613eb6565b809150509250929050565b60008060006040848603121561434857600080fd5b833561435381613eb6565b925060208401356001600160401b0381111561436e57600080fd5b61437a86828701613f55565b9497909650939450505050565b6000602080838503121561439a57600080fd5b82356001600160401b03808211156143b157600080fd5b818501915085601f8301126143c557600080fd5b8135818111156143d7576143d7614034565b8060051b91506143e884830161404a565b818152918301840191848101908884111561440257600080fd5b938501935b8385101561442057843582529385019390850190614407565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561446757835160070b83529284019291840191600101614448565b50909695505050505050565b60006020828403121561448557600080fd5b815161281181613eb6565b600082601f8301126144a157600080fd5b81516144af6141138261407a565b8181528460208386010111156144c457600080fd5b610d03826020830160208701613e3a565b6000806000606084860312156144ea57600080fd5b83516001600160401b038082111561450157600080fd5b61450d87838801614490565b9450602086015191508082111561452357600080fd5b61452f87838801614490565b9350604086015191508082111561454557600080fd5b5061455286828701614490565b9150509250925092565b6000825161456e818460208701613e3a565b6a204578697420517565756560a81b920191825250600b01919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156106165761061661458b565b634e487b7160e01b600052603260045260246000fd5b808201808211156106165761061661458b565b6001600160a01b0383168152604060208201819052600090610d0390830184613e5e565b6001600160801b03818116838216019080821115613d3a57613d3a61458b565b6001600160801b03828116828216039080821115613d3a57613d3a61458b565b60008451614653818460208901613e3a565b8083019050602f60f81b8082528551614673816001850160208a01613e3a565b6001920191820152835161468e816002840160208801613e3a565b0160020195945050505050565b607b60f81b815274226e616d65223a2245786974205469636b6574202360581b600182015283516000906146d6816016850160208901613e3a565b61088b60f21b6016918401918201527f226465736372697074696f6e223a22546869732065786974207469636b65742060188201527f63616e206265207573656420746f20636c61696d2066756e64732066726f6d2060388201527f746865206578697420717565756520636f6e7472616374206f6e63652069742060588201526e1a5cc8199d5b199a5b1b19590b888b608a1b60788201528451614782816087840160208901613e3a565b6c1134b6b0b3b2afbab936111d1160991b6087929091019182015283516147b0816094840160208801613e3a565b6147c760948284010161227d60f01b815260020190565b979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161480a81601d850160208701613e3a565b91909101601d0192915050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b63ffffffff8316815260808101612811602083018480516001600160801b03908116835260208083015182169084015260409182015116910152565b600181811c9082168061489d57607f821691505b6020821081036148bd57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156148d557600080fd5b5051919050565b63ffffffff841681526020810183905260a08101610d03604083018480516001600160801b03908116835260208083015182169084015260409182015116910152565b63ffffffff818116838216019080821115613d3a57613d3a61458b565b61ffff828116828216039080821115613d3a57613d3a61458b565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061498a90830184613e5e565b9695505050505050565b634e487b7160e01b600052601260045260246000fd5b7f2c22646973706c61795f74797065223a226e756d626572222c226d61785f7661815264363ab2911d60d91b602082015260250190565b7f2261747472696275746573223a5b7b2274726169745f74797065223a22517565815260006020743ab2903837b9b4ba34b7b71116113b30b63ab2911d60591b818401528751614a378160358601848c01613e3a565b614a456035828601016149aa565b90508751614a568183858c01613e3a565b7f7d2c7b2274726169745f74797065223a22436c61696d61626c6520616d6f756e91019081526a3a1116113b30b63ab2911d60a91b828201528651614aa181602b84018a8601613e3a565b614aaf602b828401016149aa565b9150508551614ac18183858a01613e3a565b7f7d2c7b2274726169745f74797065223a22537461747573222c2276616c7565229101908152611d1160f11b828201528451614b038160228401888601613e3a565b614b1c60228284010163089f574b60e21b815260040190565b9a9950505050505050505050565b600082614b4757634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176106165761061661458b565b601f8211156107f257600081815260208120601f850160051c81016020861015614b8a5750805b601f850160051c820191505b81811015612bd057828155600101614b96565b81516001600160401b03811115614bc257614bc2614034565b614bd681614bd08454614889565b84614b63565b602080601f831160018114614c0b5760008415614bf35750858301515b600019600386901b1c1916600185901b178555612bd0565b600085815260208120601f198616915b82811015614c3a57888601518255948401946001909101908401614c1b565b5085821015614c585787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614c7a57600080fd5b815161281181613e07565b600081614c9457614c9461458b565b506000190190565b60008351614cae818460208801613e3a565b601760f91b9083019081528351614ccc816001840160208801613e3a565b01600101949350505050565b60008351614cea818460208801613e3a565b835190830190614cfe818360208801613e3a565b01949350505050565b61181760f11b815260008251614d24816002850160208701613e3a565b919091016002019291505056fe3790264503275ecd52e8f0b419eb5ce016ca8a1f0fbac5a9ede429d0c1732004c1f66d46ebf7070ef20209d66f741219b00fb896714319503d158a28b0d103d3f9245bc1df90ea86e77b9f2423fe9cc12aa083c8ab9a55e727b285192b30d98a51fae72b3be6f7b8c2f4de519c1a9fb3f8624c4c7d1f85109b6659ae4958c29a4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fdcdd87edea8fcbdc6d50bb4863c8269eed833245e48ec3e4f64dc4cd88a272830f0463b3f5083af4c7135d28606a2c0eaa2bd9e3f9f62db1539e47244df8dc4939a5c864ceb6f99a196a385a148476994e3952fd6d71d040a2339a143eaeabe1409fdfd8838fda00128ca5d502af2ba15c034ca4130776e2ed6d3eb7811e3481a2646970667358221220a3e93748803230bbe30879ec20f4c72754463191fd9b0080061a1917085aba5864736f6c63430008110033

Deployed Bytecode

0x6080604052600436106101d85760003560e01c806395d89b4111610102578063c87b56dd11610095578063f399e22e11610064578063f399e22e14610570578063f59dfdfb14610590578063f8c21535146105a3578063fe34b92f146105d057600080fd5b8063c87b56dd146104fb578063cfbd900f1461051b578063e985e9c514610530578063eddd1bed1461055057600080fd5b8063a551878e116100d1578063a551878e14610482578063adcf11631461048e578063b88d4fde146104bb578063c129ff32146104db57600080fd5b806395d89b41146103dd5780639b21cf6c146104095780639fe9f62314610442578063a22cb4651461046257600080fd5b806342842e0e1161017a5780636352211e116101495780636352211e1461035057806370a0823114610370578063713236d51461039057806382fef84a146103b057600080fd5b806342842e0e146102e65780634cd412d5146103065780634d0392a81461031b57806353a1a2031461033b57600080fd5b8063095ea7b3116101b6578063095ea7b31461026c57806318160ddd1461028e57806323b872dd146102b157806341a52b98146102d157600080fd5b806301ffc9a7146101dd57806306fdde0314610212578063081812fc14610234575b600080fd5b3480156101e957600080fd5b506101fd6101f8366004613e1d565b6105e5565b60405190151581526020015b60405180910390f35b34801561021e57600080fd5b5061022761061c565b6040516102099190613e8a565b34801561024057600080fd5b5061025461024f366004613e9d565b610718565b6040516001600160a01b039091168152602001610209565b34801561027857600080fd5b5061028c610287366004613ecb565b610749565b005b34801561029a57600080fd5b506102a36107f7565b604051908152602001610209565b3480156102bd57600080fd5b5061028c6102cc366004613ef7565b610852565b3480156102dd57600080fd5b506102276108a1565b3480156102f257600080fd5b5061028c610301366004613ef7565b6108ba565b34801561031257600080fd5b506101fd6108d5565b34801561032757600080fd5b5061028c610336366004613e9d565b6108ff565b34801561034757600080fd5b506102a3610a0e565b34801561035c57600080fd5b5061025461036b366004613e9d565b610a26565b34801561037c57600080fd5b506102a361038b366004613f38565b610a5e565b34801561039c57600080fd5b5061028c6103ab366004613f9d565b610aa2565b3480156103bc57600080fd5b506103d06103cb366004613fde565b610bb9565b6040516102099190614004565b3480156103e957600080fd5b5060408051808201909152600381526276455160e81b6020820152610227565b34801561041557600080fd5b506104296104243660046140a1565b610c7e565b6040516001600160e01b03199091168152602001610209565b34801561044e57600080fd5b5061028c61045d366004614164565b610d0b565b34801561046e57600080fd5b5061028c61047d36600461417f565b610e20565b34801561028c57600080fd5b34801561049a57600080fd5b506104ae6104a93660046141f8565b610e2b565b6040516102099190614298565b3480156104c757600080fd5b5061028c6104d63660046140a1565b61160a565b3480156104e757600080fd5b506103d06104f6366004613e9d565b611634565b34801561050757600080fd5b50610227610516366004613e9d565b6116f0565b34801561052757600080fd5b506102a3611a5b565b34801561053c57600080fd5b506101fd61054b3660046142fa565b611a71565b34801561055c57600080fd5b506102a361056b366004613fde565b611abe565b34801561057c57600080fd5b5061028c61058b366004614333565b611af0565b61028c61059e366004613e9d565b611c44565b3480156105af57600080fd5b506105c36105be366004614387565b611e0b565b604051610209919061442c565b3480156105dc57600080fd5b506102a3611edb565b60006001600160e01b031982166380ac58cd60e01b148061061657506001600160e01b03198216635b5e139f60e01b145b92915050565b60606000610628611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190614473565b6001600160a01b031663392f37e96040518163ffffffff1660e01b8152600401600060405180830381865afa1580156106c6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106ee91908101906144d5565b5050905080604051602001610703919061455c565b60405160208183030381529060405291505090565b600061072382611f0f565b610616600080516020614d328339815191525b6000848152602091909152604090205490565b600061075482611f38565b9050806001600160a01b0316836001600160a01b0316036107985760405163b8c748d160e01b81526001600160a01b03821660048201526024015b60405180910390fd5b336001600160a01b038216148015906107b857506107b68133611a71565b155b156107e75760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b6107f2838284611f51565b505050565b60006108217f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc25490565b7f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a15461084d91906145a1565b905090565b61085c3382611fbf565b610896573361086a82611f38565b60405163295a81c160e01b81526001600160a01b0392831660048201529116602482015260440161078f565b6107f283838361201d565b606061084d600080516020614e128339815191526121d6565b6107f28383836040518060200160405280600081525061160a565b600061084d7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a05490565b600080516020614df2833981519152546001600160a01b0316336001600160a01b03161461093e573361086a600080516020614df28339815191525490565b6000610956600080516020614d928339815191525490565b9050818118818311028083189083146107f25761097b61097682846145a1565b61226e565b6040518181527f1cd073ba6c9a2801c4cf3b0aa11dde3dcbe68c2f5d750d00e09e884a4d83d4a79060200160405180910390a16109b6611ef7565b6001600160a01b0316636a3a2119826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156109f057600080fd5b505af1158015610a04573d6000803e3d6000fd5b5050505050505050565b6000600080516020614e328339815191525b54919050565b600080610a3283611f38565b90506001600160a01b0381166106165760405163ed15e6cf60e01b81526004810184905260240161078f565b6000610a69826122bc565b600080516020614d728339815191526000610a8c846001600160a01b03166122e3565b8152602001908152602001600020549050919050565b6000610aac611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0d9190614473565b6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190614473565b9050336001600160a01b03821614610baa5760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b50610bb582826122f4565b5050565b6040805160608101825260008082526020820181905291810191909152600080516020614e32833981519152805463ffffffff841610610c1457604051631cb7377f60e11b815263ffffffff8416600482015260240161078f565b808363ffffffff1681548110610c2c57610c2c6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091529392505050565b6000610c96600080516020614df28339815191525490565b6001600160a01b0316336001600160a01b031614610cc5573361086a600080516020614df28339815191525490565b610cce836123d4565b8151601403610cef57602082015160601c610ce984826123f5565b50610cf9565b610cf983856123f5565b506326c873db60e21b5b949350505050565b6000610d15611ef7565b6001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d769190614473565b6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd79190614473565b9050336001600160a01b03821614610e135760405163295a81c160e01b81523360048201526001600160a01b038216602482015260440161078f565b50610e1d816126a6565b50565b610bb5338383612701565b6060610e35613d41565b858152851580610e46575080518414155b15610e6457604051631df89e8b60e01b815260040160405180910390fd5b600080516020614e52833981519152546020820152600080516020614e328339815191525460a0808301510152856001600160401b03811115610ea957610ea9614034565b604051908082528060200260200182016040528015610ed2578160200160208202803683370190505b509150856001600160401b03811115610eed57610eed614034565b604051908082528060200260200182016040528015610f16578160200160208202803683370190505b506040820152856001600160401b03811115610f3457610f34614034565b604051908082528060200260200182016040528015610f5d578160200160208202803683370190505b5060608201526000608082018190525b81518110156114df57878782818110610f8857610f886145b4565b60a0850180516020929092029390930135905250515160801c60a083015160200152858582818110610fbc57610fbc6145b4565b9050602002016020810190610fd19190613fde565b60a08301805163ffffffff909216606090920191909152805161ffff861660c09091015251600060e0909101819052611021898984818110611015576110156145b4565b90506020020135610a26565b9050600080516020614e528339815191528360a00151602001518154811061104b5761104b6145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b90910481168585015260019091015481168483015260a0880180518301949094529251015101511690036110f55760028483815181106110bd576110bd6145b4565b602002602001019060028111156110d6576110d6614282565b908160028111156110e9576110e9614282565b90525050600101610f6d565b8260a0015160a001518360a001516060015163ffffffff161061113d5760a083015160600151604051631cb7377f60e11b815263ffffffff909116600482015260240161078f565b600080516020614e328339815191528360a001516060015163ffffffff168154811061116b5761116b6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b909104811694840194909452600101549092168183015260a08501805160809081019290925251918201519101516111d491906127cb565b61120d5760a08301518051606090910151604051633e346b6760e01b8152600481019290925263ffffffff16602482015260440161078f565b61121a8360a00151612818565b60a083015160400151602001516001600160801b03161561134f5760006112598a8a8581811061124c5761124c6145b4565b9050602002013560801c90565b905061127c8a8a85818110611270576112706145b4565b90506020020135612bd8565b60006112f382600080516020614e5283398151915284815481106112a2576112a26145b4565b60009182526020918290206040805160608101825260029390930290910180546001600160801b038082168552600160801b90910481169484018590526001909101541691015260809190911b1790565b90506112ff8382612d05565b8163ffffffff16818c8c87818110611319576113196145b4565b905060200201357f08a4aef48af435497d1effe4f4b06688b8955e5db1b5b37af57c7f18717ede8960405160405180910390a450505b60008360a0015160400151602001516001600160801b031611611373576000611376565b60015b848381518110611388576113886145b4565b602002602001019060028111156113a1576113a1614282565b908160028111156113b4576113b4614282565b90525060a083015160e00151156114d65760001960005b846080015181101561141d57826001600160a01b0316856040015182815181106113f7576113f76145b4565b60200260200101516001600160a01b0316036114155780915061141d565b6001016113cb565b50801961149d5781846040015185608001518151811061143f5761143f6145b4565b60200260200101906001600160a01b031690816001600160a01b0316815250508360a0015160e001518460600151856080015181518110611482576114826145b4565b602090810291909101015260808401805160010190526114d4565b8360a0015160e00151846060015182815181106114bc576114bc6145b4565b602002602001018181516114d091906145ca565b9052505b505b50600101610f6d565b5060005b81608001518110156115ff57600082604001518281518110611507576115076145b4565b60200260200101519050600083606001518381518110611529576115296145b4565b60200260200101519050600080836001600160a01b03168360405160006040518083038185875af1925050503d8060008114611581576040519150601f19603f3d011682016040523d82523d6000602084013e611586565b606091505b5091509150816115ad5783816040516329a5fdaf60e21b815260040161078f9291906145dd565b836001600160a01b03167fd4f43975feb89f48dd30cabbb32011045be187d1e11c8ea9faa43efc35282519846040516115e891815260200190565b60405180910390a2846001019450505050506114e3565b505095945050505050565b6116143383611fbf565b611622573361086a83611f38565b61162e84848484612e77565b50505050565b6040805160608101825260008082526020820181905291810191909152600080516020614e528339815191528054608084901c9190821061168b576040516351d7ea4f60e01b81526004810185905260240161078f565b80828154811061169d5761169d6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b9091048116948401949094526001015490921691810191909152949350505050565b60606116fb82611f0f565b60006117078360801c90565b90506000600080516020614e52833981519152828154811061172b5761172b6145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b90910481169385019390935260010154909116908201529150611789600080516020614e3283398151915290565b90506000806000808480549050116117bd576040805160608101825260008082526020820181905291810191909152611829565b835484906117cd906001906145a1565b815481106117dd576117dd6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b6020860151865191925061183c91614601565b6001600160801b031681600001516001600160801b0316111561186e5784602001516001600160801b03169250611985565b845181516001600160801b0391821691161061191457602085015185516118959190614601565b6001600160801b0316816020015182600001516118b29190614601565b6001600160801b031610156118f257602081015181516118d29190614601565b602086015186516118e39190614601565b6118ed9190614621565b6118f5565b60005b85602001516119049190614621565b6001600160801b03169250611985565b845181516001600160801b03918216911610801561195757506020810151815161193e9190614601565b6001600160801b031685600001516001600160801b0316105b156119855784516020820151825161196f9190614601565b6119799190614621565b6001600160801b031692505b602081015181516119969190614601565b6001600160801b03169150600090506119bc600080516020614e128339815191526121d6565b6119c530612eb3565b6119ce8a612ec9565b6040516020016119e093929190614641565b6040516020818303038152906040529050611a2f6119fd87612ec9565b611a08878686612f5b565b83604051602001611a1b9392919061469b565b604051602081830303815290604052612fe1565b604051602001611a3f91906147d2565b6040516020818303038152906040529650505050505050919050565b6000600080516020614e52833981519152610a20565b6001600160a01b0391821660009081527f6c716a91f6b5f5a0aa2affaf44bd88ea94ec69e363cf1fe9251e00a0fcc6c34e6020908152604080832093909416825291909152205460ff1690565b600061061663ffffffff8316600080516020614e528339815191528463ffffffff16815481106112a2576112a26145b4565b6000611b1a7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b8103611bf957611b52611b2e8260016145ca565b7fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a7655565b7f91efa3d50feccde0d0d202f8ae5c41ca0b2be614cebcb2bd2f4b019396e6568a81600036604051611b8693929190614817565b60405180910390a1611b9883836122f4565b611ba1846122bc565b611bb8600080516020614df2833981519152859055565b6040516001600160a01b03851681527f67816c9262630d6052ccaada1732fda377aa9e9abb3bc91cfd887a016a18a43a9060200160405180910390a161162e565b80611c227fc4c7f1ccb588f39a9aa57be6cfd798d73912e27b44cfa18e1a5eba7b34e81a765490565b60405163031b997760e51b81526004810192909252602482015260440161078f565b600080516020614df2833981519152546001600160a01b0316336001600160a01b031614611c83573361086a600080516020614df28339815191525490565b611c8c816123d4565b611c95346123d4565b600080516020614e328339815191528054600081611ccf576040805160608101825260008082526020820181905291810191909152611d37565b82611cdb6001846145a1565b81548110611ceb57611ceb6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b90506000604051806060016040528083602001518460000151611d5a9190614601565b6001600160801b039081168252878116602080840191909152348216604093840152875460018181018a5560008a81528390208651938701518516600160801b029385169390931760029092029092019081558484015191018054919092166001600160801b031991909116179055519091507f7833b416621d0b35128379673a1aa96c979020b58203e7b60c5c93f4d52a7f6c90611dfc908590849061484d565b60405180910390a15050505050565b8051606090806001600160401b03811115611e2857611e28614034565b604051908082528060200260200182016040528015611e51578160200160208202803683370190505b50600080516020614e5283398151915254600080516020614e32833981519152549193509060005b83811015611ed257611ea5868281518110611e9657611e966145b4565b60200260200101518484613133565b858281518110611eb757611eb76145b4565b60079290920b60209283029190910190910152600101611e79565b50505050919050565b600061084d600080516020614d928339815191525490565b9055565b600061084d600080516020614df28339815191525490565b611f18816132b3565b610e1d5760405163ed15e6cf60e01b81526004810182905260240161078f565b6000610616600080516020614d52833981519152610736565b611f63836001600160a01b03166122e3565b6000828152600080516020614d32833981519152602052604080822092909255905182916001600160a01b0386811692908616917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b600080611fcb83611f38565b9050806001600160a01b0316846001600160a01b03161480611ff25750611ff28185611a71565b80610d035750836001600160a01b031661200b84610718565b6001600160a01b031614949350505050565b6001600160a01b0382166120445760405163304bef0b60e01b815260040160405180910390fd5b826001600160a01b031661205782611f38565b6001600160a01b03161461209b5761206e81611f38565b60405163295a81c160e01b81526001600160a01b039182166004820152908416602482015260440161078f565b6120a68383836132d0565b826001600160a01b03166120b982611f38565b6001600160a01b0316146120d05761206e81611f38565b600080516020614d32833981519152600082815260209190915260408120556001600080516020614d728339815191526000612114866001600160a01b03166122e3565b8152602081019190915260400160002080549190910390556001600080516020614d728339815191526000612151856001600160a01b03166122e3565b815260208101919091526040016000208054909101905561217a6001600160a01b0383166122e3565b6000828152600080516020614d52833981519152602052604080822092909255905182916001600160a01b0385811692908716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8054606090829081906121e890614889565b80601f016020809104026020016040519081016040528092919081815260200182805461221490614889565b80156122615780601f1061223657610100808354040283529160200191612261565b820191906000526020600020905b81548152906001019060200180831161224457829003601f168201915b5050505050915050919050565b612285600080516020614d92833981519152829055565b6040518181527ffc7d134b2e716a81746c1abdbababc8c42ec12a09a1ed70f07f27bdb3646e66d906020015b60405180910390a150565b6001600160a01b038116610e1d5760405163f6b2911f60e01b815260040160405180910390fd5b60006001600160a01b038216610616565b61233382828080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061330f92505050565b61238382828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600080516020614e12833981519152939250506133319050565b7fdffd2b831b1643e102b26bc49c317a42cd527eb1e6fd09ced85ed26ca8aaaec36123bb600080516020614e128339815191526121d6565b6040516123c89190613e8a565b60405180910390a15050565b80600003610e1d5760405163095e705160e11b815260040160405180910390fd5b60006123ff611ef7565b90506000816001600160a01b031663143a08d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612441573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246591906148c3565b90506000826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124cb91906148c3565b600080516020614e52833981519152805491925090600081612509576040805160608101825260008082526020820181905291810191909152612571565b826125156001846145a1565b81548110612525576125256145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091525b905060006040518060600160405280836020015184600001516125949190614601565b6001600160801b031681526020018a6001600160801b031681526020016125c58b6001600160801b0316898961333d565b6001600160801b03169052905060006125f08483602001516001600160801b031660809190911b1790565b85546001808201885560008881526020908190208651918701516001600160801b03928316600160801b918416919091021760029094020192835560408601519290910180546001600160801b0319169290911691909117905590506126568982612d05565b886001600160a01b03167e80df45f12186856da484a1494bb51907e2abec5abc9a401e443c116bed71a5858385604051612692939291906148dc565b60405180910390a250505050505050505050565b6126cf7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a0829055565b60405181151581527f5bff65e3b00078a5ca87389c773003039a08945fe60195f722c109a37c4cca4f906020016122b1565b816001600160a01b0316836001600160a01b03160361273e5760405163b8c748d160e01b81526001600160a01b038416600482015260240161078f565b6001600160a01b0383811660008181527f6c716a91f6b5f5a0aa2affaf44bd88ea94ec69e363cf1fe9251e00a0fcc6c34e6020908152604080832094871680845294825291829020805460ff1916861515908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050565b602081015181516000916127de91614601565b6001600160801b031683600001516001600160801b03161080156128115750815183516001600160801b03918216911610155b9392505050565b60808101516020810151905160009161283091614601565b6040830151602081015190516001600160801b0392831693506000926128699281169161285e9116856145a1565b808218908211021890565b905060006128a3826001600160801b03168560800151604001516001600160801b03168660800151602001516001600160801b031661333d565b905060006128dd836001600160801b03168660400151604001516001600160801b03168760400151602001516001600160801b031661333d565b90506000826001600160801b0316826001600160801b0316101561293a576129058284614621565b6001600160801b0316905081925061293a81612930600080516020614d9283398151915260001b5490565b61097691906145ca565b8386604001516000018181516129509190614601565b6001600160801b031690525060408601516020018051859190612974908390614621565b6001600160801b0316905250604080870151018051849190612997908390614621565b6001600160801b0390811690915260e0880180519186169250906129bc9083906145ca565b9052506060868101518751604080516001600160801b03808a1682528816602082015290810185905263ffffffff9092169290917fef3abb819e31c9009302363931f93286b338da7cab0c25e6f948c7955fd5fd44910160405180910390a360008660400151602001516001600160801b0316118015612a54575060a08601516060870151612a4c90600161491f565b63ffffffff16105b8015612a68575060008660c0015161ffff16115b15612b2857600186606001818151612a80919061491f565b63ffffffff16905250600080516020614e32833981519152866060015163ffffffff1681548110612ab357612ab36145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260019081015490931690820152608088015260c087018051612b1390839061493c565b61ffff16905250612b2386612818565b612bd0565b8560400151602001516001600160801b0316600003612b4c578551612b4c90612bd8565b6000600080516020614e528339815191529050866040015181886020015181548110612b7a57612b7a6145b4565b60009182526020918290208351928401516001600160801b03938416600160801b918516919091021760029290920201908155604090920151600190920180546001600160801b03191692909116919091179055505b505050505050565b612be181611f0f565b612bea81611f0f565b6000612c03600080516020614d52833981519152610736565b90506001600080516020614d728339815191526000612c2a846001600160a01b03166122e3565b815260208101919091526040016000208054919091039055612c94612c6d7f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc25490565b6001017f0644144c18bf2aa8e15d5433cc3f6e2273ab9ccd122cd4f430275a2997cc0dc255565b6000828152600080516020614d3283398151915260209081526040808320839055600080516020614d52833981519152909152808220829055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6001600160a01b038216612d2b5760405162f5345360e01b815260040160405180910390fd5b612d34816132b3565b15612d55576040516322d1d39560e21b81526004810182905260240161078f565b612d5e816132b3565b15612d7f576040516322d1d39560e21b81526004810182905260240161078f565b6001600080516020614d728339815191526000612da4856001600160a01b03166122e3565b8152602081019190915260400160002080549091019055612e0d612de67f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a15490565b6001017f3d706fc25ad0e96a2c3fb1b58cdd70ba377f331d59f761caecaf2f3a236d99a155565b612e1f826001600160a01b03166122e3565b6000828152600080516020614d52833981519152602052604080822092909255905182916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b612e8284848461201d565b612e8e8484848461334a565b61162e5783838383604051630eefeeed60e21b815260040161078f9493929190614957565b60606106166001600160a01b0383166014613451565b60606000612ed6836135ec565b60010190506000816001600160401b03811115612ef557612ef5614034565b6040519080825280601f01601f191660200182016040528015612f1f576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084612f2957509392505050565b6060612f7784600001516001600160801b0316601260036136c4565b612f8483601260036136c4565b612f9185601260036136c4565b612fab87602001516001600160801b0316601260036136c4565b612fb5888861385a565b604051602001612fc99594939291906149e1565b60405160208183030381529060405290509392505050565b6060815160000361300057505060408051602081019091526000815290565b6000604051806060016040528060408152602001614db2604091399050600060038451600261302f91906145ca565b6130399190614b2a565b613044906004614b4c565b6001600160401b0381111561305b5761305b614034565b6040519080825280601f01601f191660200182016040528015613085576020820181803683370190505b509050600182016020820185865187015b808210156130f1576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250613096565b505060038651066001811461310d576002811461312057613128565b603d6001830353603d6002830353613128565b603d60018303535b509195945050505050565b6000806131408560801c90565b905083811061315457600019915050612811565b6000600080516020614e528339815191528281548110613176576131766145b4565b6000918252602080832060408051606081018252600290940290910180546001600160801b038082168652600160801b9091048116938501849052600190910154169083015290925090036131d15760011992505050612811565b831580613289575080516001600160801b0316600080516020614e328339815191526131fe6001876145a1565b8154811061320e5761320e6145b4565b6000918252602090912060029091020154600160801b90046001600160801b0316600080516020614e328339815191526132496001886145a1565b81548110613259576132596145b4565b600091825260209091206002909102015461327d91906001600160801b0316614601565b6001600160801b031611155b1561329a5760021992505050612811565b6132a382613904565b63ffffffff169695505050505050565b6000806132bf83611f38565b6001600160a01b0316141592915050565b7fc1bfc3030aebadb3bfaa3fbc59cf364f7dee6ab92429159a4bfdf02fa88336a0546107f25760405163a24e573d60e01b815260040160405180910390fd5b8051600003610e1d57604051638d46fe0560e01b815260040160405180910390fd5b818061162e8382614ba9565b6000610d03848484613b21565b60006001600160a01b0384163b1561344957604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061338e903390899088908890600401614957565b6020604051808303816000875af19250505080156133c9575060408051601f3d908101601f191682019092526133c691810190614c68565b60015b61342f573d8080156133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5080516000036134275785858585604051630eefeeed60e21b815260040161078f9493929190614957565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610d03565b506001610d03565b60606000613460836002614b4c565b61346b9060026145ca565b6001600160401b0381111561348257613482614034565b6040519080825280601f01601f1916602001820160405280156134ac576020820181803683370190505b509050600360fc1b816000815181106134c7576134c76145b4565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106134f6576134f66145b4565b60200101906001600160f81b031916908160001a905350600061351a846002614b4c565b6135259060016145ca565b90505b600181111561359d576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613559576135596145b4565b1a60f81b82828151811061356f5761356f6145b4565b60200101906001600160f81b031916908160001a90535060049490941c9361359681614c85565b9050613528565b5083156128115760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161078f565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061362b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310613657576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061367557662386f26fc10000830492506010015b6305f5e100831061368d576305f5e100830492506008015b61271083106136a157612710830492506004015b606483106136b3576064830492506002015b600a83106106165760010192915050565b6060836000036136ec57506040805180820190915260018152600360fc1b6020820152612811565b60006136f785612ec9565b90508360ff1660000361370b579050612811565b805160ff85168111156137a5578360ff166000036137445761373b82600061373660ff8916856145a1565b613bee565b92505050612811565b61375782600061373660ff8916856145a1565b61377e8361376860ff8916856145a1565b6137368960ff168960ff16808218908211021890565b60405160200161378f929190614c9c565b6040516020818303038152906040529150613851565b6137b28160ff87166145a1565b8460ff16116137de57604051806040016040528060018152602001600360fc1b81525092505050612811565b61382f6137f66137f18360ff89166145a1565b613cb0565b83604051602001613808929190614cd8565b60405160208183030381529060405260006137368860ff168860ff16808218908211021890565b60405160200161383f9190614d07565b60405160208183030381529060405291505b50949350505050565b60608160000361389257506040805180820190915260118152704e6f742079657420636c61696d61626c6560781b6020820152610616565b82602001516001600160801b03168210156138d757506040805180820190915260138152725061727469616c6c7920636c61696d61626c6560681b6020820152610616565b5060408051808201909152600f81526e46756c6c7920636c61696d61626c6560881b602082015292915050565b600080516020614e328339815191528054600091908290613927906001906145a1565b90506000600080516020614e52833981519152858154811061394b5761394b6145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091528354909150613a09908290859063ffffffff86169081106139b9576139b96145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091526127cb565b15613a1657509392505050565b6000613a3482858363ffffffff16815481106139b9576139b96145b4565b15613a425795945050505050565b8263ffffffff168163ffffffff1614613b185760006001613a63858461491f565b63ffffffff16901c90506000858263ffffffff1681548110613a8757613a876145b4565b600091825260209182902060408051606081018252600290930290910180546001600160801b038082168552600160801b90910481169484019490945260010154909216918101919091529050613ade84826127cb565b15613aee57509695505050505050565b805184516001600160801b0391821691161015613b0d57819450613b11565b8192505b5050613a42565b95945050505050565b6000808060001985870985870292508281108382030391505080600003613b5b57838281613b5157613b51614994565b0492505050612811565b838110613b8557604051631dcf306360e21b8152600481018290526024810185905260440161078f565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60608182601f011015613c14576040516323d5783d60e11b815260040160405180910390fd5b613c1e82846145ca565b84511015613c3f57604051633b99b53d60e01b815260040160405180910390fd5b606082158015613c5e5760405191506000825260208201604052613851565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613c97578051835260209283019201613c7f565b5050858452601f01601f19166040525050949350505050565b60606000826001600160401b03811115613ccc57613ccc614034565b6040519080825280601f01601f191660200182016040528015613cf6576020820181803683370190505b50905060005b83811015613d3a57600360fc1b828281518110613d1b57613d1b6145b4565b60200101906001600160f81b031916908160001a905350600101613cfc565b5092915050565b6040518060c001604052806000815260200160008152602001606081526020016060815260200160008152602001613d77613d7c565b905290565b6040518061010001604052806000815260200160008152602001613db9604080516060810182526000808252602082018190529181019190915290565b815260006020820152604001613de8604080516060810182526000808252602082018190529181019190915290565b815260200160008152602001600061ffff168152602001600081525090565b6001600160e01b031981168114610e1d57600080fd5b600060208284031215613e2f57600080fd5b813561281181613e07565b60005b83811015613e55578181015183820152602001613e3d565b50506000910152565b60008151808452613e76816020860160208601613e3a565b601f01601f19169290920160200192915050565b6020815260006128116020830184613e5e565b600060208284031215613eaf57600080fd5b5035919050565b6001600160a01b0381168114610e1d57600080fd5b60008060408385031215613ede57600080fd5b8235613ee981613eb6565b946020939093013593505050565b600080600060608486031215613f0c57600080fd5b8335613f1781613eb6565b92506020840135613f2781613eb6565b929592945050506040919091013590565b600060208284031215613f4a57600080fd5b813561281181613eb6565b60008083601f840112613f6757600080fd5b5081356001600160401b03811115613f7e57600080fd5b602083019150836020828501011115613f9657600080fd5b9250929050565b60008060208385031215613fb057600080fd5b82356001600160401b03811115613fc657600080fd5b613fd285828601613f55565b90969095509350505050565b600060208284031215613ff057600080fd5b813563ffffffff8116811461281157600080fd5b60608101610616828480516001600160801b03908116835260208083015182169084015260409182015116910152565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561407257614072614034565b604052919050565b60006001600160401b0382111561409357614093614034565b50601f01601f191660200190565b600080600080608085870312156140b757600080fd5b84356140c281613eb6565b935060208501356140d281613eb6565b92506040850135915060608501356001600160401b038111156140f457600080fd5b8501601f8101871361410557600080fd5b80356141186141138261407a565b61404a565b81815288602083850101111561412d57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b8035801515811461415f57600080fd5b919050565b60006020828403121561417657600080fd5b6128118261414f565b6000806040838503121561419257600080fd5b823561419d81613eb6565b91506141ab6020840161414f565b90509250929050565b60008083601f8401126141c657600080fd5b5081356001600160401b038111156141dd57600080fd5b6020830191508360208260051b8501011115613f9657600080fd5b60008060008060006060868803121561421057600080fd5b85356001600160401b038082111561422757600080fd5b61423389838a016141b4565b9097509550602088013591508082111561424c57600080fd5b50614259888289016141b4565b909450925050604086013561ffff8116811461427457600080fd5b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000919084820190604085019084805b828110156142ed578451600381106142db57634e487b7160e01b83526021600452602483fd5b845293850193928501926001016142b5565b5091979650505050505050565b6000806040838503121561430d57600080fd5b823561431881613eb6565b9150602083013561432881613eb6565b809150509250929050565b60008060006040848603121561434857600080fd5b833561435381613eb6565b925060208401356001600160401b0381111561436e57600080fd5b61437a86828701613f55565b9497909650939450505050565b6000602080838503121561439a57600080fd5b82356001600160401b03808211156143b157600080fd5b818501915085601f8301126143c557600080fd5b8135818111156143d7576143d7614034565b8060051b91506143e884830161404a565b818152918301840191848101908884111561440257600080fd5b938501935b8385101561442057843582529385019390850190614407565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561446757835160070b83529284019291840191600101614448565b50909695505050505050565b60006020828403121561448557600080fd5b815161281181613eb6565b600082601f8301126144a157600080fd5b81516144af6141138261407a565b8181528460208386010111156144c457600080fd5b610d03826020830160208701613e3a565b6000806000606084860312156144ea57600080fd5b83516001600160401b038082111561450157600080fd5b61450d87838801614490565b9450602086015191508082111561452357600080fd5b61452f87838801614490565b9350604086015191508082111561454557600080fd5b5061455286828701614490565b9150509250925092565b6000825161456e818460208701613e3a565b6a204578697420517565756560a81b920191825250600b01919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156106165761061661458b565b634e487b7160e01b600052603260045260246000fd5b808201808211156106165761061661458b565b6001600160a01b0383168152604060208201819052600090610d0390830184613e5e565b6001600160801b03818116838216019080821115613d3a57613d3a61458b565b6001600160801b03828116828216039080821115613d3a57613d3a61458b565b60008451614653818460208901613e3a565b8083019050602f60f81b8082528551614673816001850160208a01613e3a565b6001920191820152835161468e816002840160208801613e3a565b0160020195945050505050565b607b60f81b815274226e616d65223a2245786974205469636b6574202360581b600182015283516000906146d6816016850160208901613e3a565b61088b60f21b6016918401918201527f226465736372697074696f6e223a22546869732065786974207469636b65742060188201527f63616e206265207573656420746f20636c61696d2066756e64732066726f6d2060388201527f746865206578697420717565756520636f6e7472616374206f6e63652069742060588201526e1a5cc8199d5b199a5b1b19590b888b608a1b60788201528451614782816087840160208901613e3a565b6c1134b6b0b3b2afbab936111d1160991b6087929091019182015283516147b0816094840160208801613e3a565b6147c760948284010161227d60f01b815260020190565b979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161480a81601d850160208701613e3a565b91909101601d0192915050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b63ffffffff8316815260808101612811602083018480516001600160801b03908116835260208083015182169084015260409182015116910152565b600181811c9082168061489d57607f821691505b6020821081036148bd57634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156148d557600080fd5b5051919050565b63ffffffff841681526020810183905260a08101610d03604083018480516001600160801b03908116835260208083015182169084015260409182015116910152565b63ffffffff818116838216019080821115613d3a57613d3a61458b565b61ffff828116828216039080821115613d3a57613d3a61458b565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061498a90830184613e5e565b9695505050505050565b634e487b7160e01b600052601260045260246000fd5b7f2c22646973706c61795f74797065223a226e756d626572222c226d61785f7661815264363ab2911d60d91b602082015260250190565b7f2261747472696275746573223a5b7b2274726169745f74797065223a22517565815260006020743ab2903837b9b4ba34b7b71116113b30b63ab2911d60591b818401528751614a378160358601848c01613e3a565b614a456035828601016149aa565b90508751614a568183858c01613e3a565b7f7d2c7b2274726169745f74797065223a22436c61696d61626c6520616d6f756e91019081526a3a1116113b30b63ab2911d60a91b828201528651614aa181602b84018a8601613e3a565b614aaf602b828401016149aa565b9150508551614ac18183858a01613e3a565b7f7d2c7b2274726169745f74797065223a22537461747573222c2276616c7565229101908152611d1160f11b828201528451614b038160228401888601613e3a565b614b1c60228284010163089f574b60e21b815260040190565b9a9950505050505050505050565b600082614b4757634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176106165761061661458b565b601f8211156107f257600081815260208120601f850160051c81016020861015614b8a5750805b601f850160051c820191505b81811015612bd057828155600101614b96565b81516001600160401b03811115614bc257614bc2614034565b614bd681614bd08454614889565b84614b63565b602080601f831160018114614c0b5760008415614bf35750858301515b600019600386901b1c1916600185901b178555612bd0565b600085815260208120601f198616915b82811015614c3a57888601518255948401946001909101908401614c1b565b5085821015614c585787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614c7a57600080fd5b815161281181613e07565b600081614c9457614c9461458b565b506000190190565b60008351614cae818460208801613e3a565b601760f91b9083019081528351614ccc816001840160208801613e3a565b01600101949350505050565b60008351614cea818460208801613e3a565b835190830190614cfe818360208801613e3a565b01949350505050565b61181760f11b815260008251614d24816002850160208701613e3a565b919091016002019291505056fe3790264503275ecd52e8f0b419eb5ce016ca8a1f0fbac5a9ede429d0c1732004c1f66d46ebf7070ef20209d66f741219b00fb896714319503d158a28b0d103d3f9245bc1df90ea86e77b9f2423fe9cc12aa083c8ab9a55e727b285192b30d98a51fae72b3be6f7b8c2f4de519c1a9fb3f8624c4c7d1f85109b6659ae4958c29a4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fdcdd87edea8fcbdc6d50bb4863c8269eed833245e48ec3e4f64dc4cd88a272830f0463b3f5083af4c7135d28606a2c0eaa2bd9e3f9f62db1539e47244df8dc4939a5c864ceb6f99a196a385a148476994e3952fd6d71d040a2339a143eaeabe1409fdfd8838fda00128ca5d502af2ba15c034ca4130776e2ed6d3eb7811e3481a2646970667358221220a3e93748803230bbe30879ec20f4c72754463191fd9b0080061a1917085aba5864736f6c63430008110033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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