false
true
0

Contract Address Details

0x004564d44E6921FFA68936F44ae58988Cd146b10

Contract Name
Raffle
Creator
0xaf2b21–3e4225 at 0x6bd973–79897e
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
26783732
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
Raffle




Optimization enabled
true
Compiler version
v0.8.25+commit.b61c2a91




Optimization runs
1000
EVM Version
shanghai




Verified at
2026-06-11T17:53:27.638714Z

Constructor Arguments

00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61

Arg [0] (address) : 0x87fc31413534733a09df5dc5aa33b4dba1f64b61

              

contracts/Raffle.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {GameBase} from "./GameBase.sol";
import {IRandom} from "./implementations/IRandom.sol";
import {PreimageLocation} from "./PreimageLocation.sol";

/// @notice Closest-guess raffle on validator-only entropy. Players commit a hidden, address-bound
/// guess in [1..256] at an equal per-guess stake into a round keyed by its parameter tuple. When
/// the round has at least its threshold of commits and a period has elapsed, an operator arms it
/// (heating the declared validator subset through GameBase's bound heat). A validator cast sets the
/// seed; the contract records the draw and opens a claim window in which committers reveal; the
/// closest revealed guess takes the pot less the fee. Non-revealers forfeit; nothing a player does
/// can stall, abort, or grind the draw.
contract Raffle is GameBase {
    error BadParams();
    error NotFilling();
    error ThresholdNotMet();
    error PeriodNotElapsed();
    error NotTicketOwner();
    error TicketInactive();
    error WrongRoundState();
    error WindowClosed();
    error WindowOpen();
    error BadReveal();
    error AlreadyRevealed();
    error GuessOutOfRange();
    error TooEarly();
    error NothingToRefund();
    error BadFee();

    event RoundOpened(bytes32 indexed roundId, uint256 stake, uint256 threshold, uint256 period, bytes32 subsetHash);
    event Committed(uint256 indexed ticketId, bytes32 indexed roundId, address indexed player, bytes32 commitment);
    event TicketCancelled(uint256 indexed ticketId);
    event Armed(bytes32 indexed roundId, bytes32 indexed key);
    event Drawn(bytes32 indexed roundId, uint256 draw, uint256 claimDeadline);
    event Revealed(uint256 indexed ticketId, bytes32 indexed roundId, uint256 guess, uint256 distance, bool leading);
    event Finalised(bytes32 indexed roundId, address indexed winner, uint256 payout, uint256 fee);
    event NoContest(bytes32 indexed roundId, uint256 potPerValidator);
    event TicketRefunded(uint256 indexed ticketId);

    enum Status { None, Filling, Drawing, Claiming, Paid, Refunded }

    struct Round {
        uint256 stake;
        uint256 threshold;
        uint256 period;
        bytes32 subsetHash;
        uint256 createdAtBlock;
        uint256 commitCount;
        uint256 pot;
        Status status;
        bytes32 key;
        uint256 armedAtBlock;
        uint256 draw;
        uint256 claimDeadline;
        uint256 bestTicket;
        uint256 bestDistance;
        uint256 settledPot;
    }

    struct Ticket {
        bytes32 roundId;
        address player;
        bytes32 commitment;
        uint256 committedAtBlock;
        bool active;
        bool revealed;
    }

    uint256 public constant RANGE = 256; // draws and guesses are in [1..256]
    uint256 public constant CLAIM_BLOCKS = 100;
    uint256 public constant BIPS = 10_000;

    /// @notice owner-adjustable rake, in basis points, default zero; a percentage so a nonzero
    /// value self-taxes raffle flooding.
    uint256 public feeBips;
    address public feeRecipient;

    mapping(bytes32 roundId => Round) public rounds;
    mapping(bytes32 roundId => address[] subset) internal _roundSubset;
    mapping(uint256 ticketId => Ticket) public tickets;
    uint256 public nextTicket;

    /// @notice the currently-filling round for a parameter tuple, so commits with the same tuple
    /// concentrate into one round; cleared when that round arms so the next commit opens a fresh one.
    mapping(bytes32 tupleHash => bytes32 roundId) public activeRound;
    uint256 internal _roundNonce;

    constructor(address _random) GameBase(_random) {
        feeRecipient = msg.sender;
    }

    function setFee(uint256 newFeeBips, address newRecipient) external onlyOwner {
        if (newFeeBips > BIPS) revert BadFee();
        feeBips = newFeeBips;
        feeRecipient = newRecipient;
    }

    /// @notice Commit a hidden guess into the round for these parameters at the sent stake.
    /// commitment = keccak256(abi.encode(guess, salt, msg.sender)). Opens a new round if none is
    /// filling for this tuple.
    function commit(
        uint256 stake,
        uint256 threshold,
        uint256 period,
        address[] calldata validatorSubset,
        bytes32 commitment
    ) external payable returns (uint256 ticketId) {
        if (stake == 0 || threshold == 0 || period == 0) revert BadParams();
        _take(stake);
        _validateSubset(validatorSubset);
        bytes32 subsetHash = keccak256(abi.encode(validatorSubset));
        bytes32 tupleHash = keccak256(abi.encode(stake, threshold, period, subsetHash));

        bytes32 roundId = activeRound[tupleHash];
        if (roundId == bytes32(0) || rounds[roundId].status != Status.Filling) {
            roundId = keccak256(abi.encode(address(this), ++_roundNonce, tupleHash));
            rounds[roundId] = Round({
                stake: stake,
                threshold: threshold,
                period: period,
                subsetHash: subsetHash,
                createdAtBlock: block.number,
                commitCount: 0,
                pot: 0,
                status: Status.Filling,
                key: bytes32(0),
                armedAtBlock: 0,
                draw: 0,
                claimDeadline: 0,
                bestTicket: 0,
                bestDistance: 0,
                settledPot: 0
            });
            _roundSubset[roundId] = validatorSubset;
            activeRound[tupleHash] = roundId;
            emit RoundOpened(roundId, stake, threshold, period, subsetHash);
        }

        Round storage round = rounds[roundId];
        ticketId = ++nextTicket;
        tickets[ticketId] = Ticket({
            roundId: roundId,
            player: msg.sender,
            commitment: commitment,
            committedAtBlock: block.number,
            active: true,
            revealed: false
        });
        unchecked {
            ++round.commitCount;
            round.pot += stake;
        }
        emit Committed(ticketId, roundId, msg.sender, commitment);
    }

    /// @notice Reclaim a still-waiting ticket while its round is filling (the per-ticket escape).
    function cancel(uint256 ticketId) external {
        Ticket storage ticket = tickets[ticketId];
        if (ticket.player != msg.sender) revert NotTicketOwner();
        if (!ticket.active) revert TicketInactive();
        Round storage round = rounds[ticket.roundId];
        if (round.status != Status.Filling) revert WrongRoundState();
        ticket.active = false;
        unchecked {
            --round.commitCount;
            round.pot -= round.stake;
        }
        emit TicketCancelled(ticketId);
        _refund(ticket.player, round.stake);
    }

    function roundSubset(bytes32 roundId) external view returns (address[] memory) {
        return _roundSubset[roundId];
    }

    /// @notice Operator step: heat the round's declared validator subset once it is at threshold and
    /// a period has elapsed. Permissionless, but bound — _heatBound permits only locations matching
    /// the declared subset, and the threshold and filling-status checks make arming one-shot.
    function arm(bytes32 roundId, PreimageLocation.Info[] calldata validatorLocations) external {
        Round storage round = rounds[roundId];
        if (round.status != Status.Filling) revert NotFilling();
        if (round.commitCount < round.threshold) revert ThresholdNotMet();
        if (block.number < round.createdAtBlock + round.period) revert PeriodNotElapsed();

        round.status = Status.Drawing;
        round.armedAtBlock = block.number;
        round.settledPot = round.pot;
        // the next commit for this tuple opens a fresh round
        bytes32 tupleHash = keccak256(abi.encode(round.stake, round.threshold, round.period, round.subsetHash));
        if (activeRound[tupleHash] == roundId) {
            activeRound[tupleHash] = bytes32(0);
        }

        bytes32 key = _heatBound(_roundSubset[roundId], validatorLocations);
        round.key = key;
        instanceByKey[key] = roundId;
        emit Armed(roundId, key);
    }

    /// @notice Record the draw and open the claim window (does not pay). Invoked by onCast (push)
    /// via GameBase and by recordDraw (pull fallback).
    function _settle(bytes32 roundId, bytes32 seed) internal override {
        Round storage round = rounds[roundId];
        if (round.status != Status.Drawing) revert WrongRoundState();
        round.status = Status.Claiming;
        round.draw = 1 + (uint256(seed) % RANGE);
        round.claimDeadline = block.number + CLAIM_BLOCKS;
        emit Drawn(roundId, round.draw, round.claimDeadline);
    }

    /// @notice Reveal a committed guess during the claim window. Verifies the commitment against
    /// (guess, salt, msg.sender) — the address binding is what stops a front-runner replaying a
    /// revealed guess from the mempool. Overwrites the provisional winner if strictly closer, ties
    /// broken by earliest commit block then ticket id (so the winner is independent of reveal order).
    function reveal(uint256 ticketId, uint256 guess, bytes32 salt) external {
        Ticket storage ticket = tickets[ticketId];
        Round storage round = rounds[ticket.roundId];
        if (round.status != Status.Claiming) revert WrongRoundState();
        if (block.number > round.claimDeadline) revert WindowClosed();
        if (!ticket.active) revert TicketInactive();
        if (ticket.revealed) revert AlreadyRevealed();
        if (guess < 1 || guess > RANGE) revert GuessOutOfRange();
        if (keccak256(abi.encode(guess, salt, msg.sender)) != ticket.commitment) revert BadReveal();

        ticket.revealed = true;
        uint256 distance = guess > round.draw ? guess - round.draw : round.draw - guess;

        bool leading;
        if (round.bestTicket == 0) {
            leading = true;
        } else if (distance < round.bestDistance) {
            leading = true;
        } else if (distance == round.bestDistance) {
            Ticket storage best = tickets[round.bestTicket];
            if (ticket.committedAtBlock < best.committedAtBlock) {
                leading = true;
            } else if (ticket.committedAtBlock == best.committedAtBlock && ticketId < round.bestTicket) {
                leading = true;
            }
        }
        if (leading) {
            round.bestTicket = ticketId;
            round.bestDistance = distance;
        }
        emit Revealed(ticketId, ticket.roundId, guess, distance, leading);
    }

    /// @notice Pull fallback when the onCast push did not complete though the seed is finalized.
    function recordDraw(bytes32 roundId) external {
        Round storage round = rounds[roundId];
        if (round.status != Status.Drawing) revert WrongRoundState();
        bytes32 seed = IRandom(random).randomness(round.key).seed;
        if (seed == bytes32(0)) revert TooEarly();
        _settle(roundId, seed);
    }

    /// @notice After the claim window, pay the closest revealer the pot less the fee; if nobody
    /// revealed, route the pot to the round's contributing validators (the no-contest case — the
    /// seed finalised, so no validator was chopped). Non-revealers' stakes are part of the pot.
    function finalise(bytes32 roundId) external {
        Round storage round = rounds[roundId];
        if (round.status != Status.Claiming) revert WrongRoundState();
        if (block.number <= round.claimDeadline) revert WindowOpen();
        round.status = Status.Paid;

        uint256 pot = round.settledPot;
        if (round.bestTicket == 0) {
            address[] storage subset = _roundSubset[roundId];
            uint256 n = subset.length;
            uint256 share = pot / n;
            for (uint256 i = 0; i < n; ++i) {
                _pay(subset[i], i + 1 == n ? pot - share * (n - 1) : share);
            }
            emit NoContest(roundId, share);
            return;
        }

        uint256 fee = (pot * feeBips) / BIPS;
        address winner = tickets[round.bestTicket].player;
        emit Finalised(roundId, winner, pot - fee, fee);
        if (fee > 0) _pay(feeRecipient, fee);
        _pay(winner, pot - fee);
    }

    /// @notice Liveness exit: when an armed round's seed never finalised (chopped or stale), each
    /// committer reclaims their own ticket's stake. Pull, per-ticket, so a large round needs no push.
    function refundTicket(uint256 ticketId) external {
        Ticket storage ticket = tickets[ticketId];
        if (ticket.player != msg.sender) revert NotTicketOwner();
        if (!ticket.active) revert TicketInactive();
        Round storage round = rounds[ticket.roundId];
        if (round.status != Status.Drawing) revert WrongRoundState();
        bool seedMissing = IRandom(random).randomness(round.key).seed == bytes32(0);
        if (!seedMissing) revert TooEarly();
        if (!choppedInstance[ticket.roundId] && !_isStale(round.armedAtBlock)) revert TooEarly();
        ticket.active = false;
        emit TicketRefunded(ticketId);
        _refund(ticket.player, round.stake);
    }
}
        

/SafeTransferLib.sol

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /// @dev The Permit2 approve operation has failed.
    error Permit2ApproveFailed();

    /// @dev The Permit2 lockdown operation has failed.
    error Permit2LockdownFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /// @dev The canonical address of the `SELFDESTRUCT` ETH mover.
    /// See: https://gist.github.com/Vectorized/1cb8ad4cf393b1378e08f23f79bd99fa
    /// [Etherscan](https://etherscan.io/address/0x00000000000073c48c8055bD43D1A53799176f0D)
    address internal constant ETH_MOVER = 0x00000000000073c48c8055bD43D1A53799176f0D;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Force transfers ETH to `to`, without triggering the fallback (if any).
    /// This method attempts to use a separate contract to send via `SELFDESTRUCT`,
    /// and upon failure, deploys a minimal vault to accrue the ETH.
    function safeMoveETH(address to, uint256 amount) internal returns (address vault) {
        /// @solidity memory-safe-assembly
        assembly {
            to := shr(96, shl(96, to)) // Clean upper 96 bits.
            for { let mover := ETH_MOVER } iszero(eq(to, address())) {} {
                let selfBalanceBefore := selfbalance()
                if or(lt(selfBalanceBefore, amount), eq(to, mover)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                if extcodesize(mover) {
                    let balanceBefore := balance(to) // Check via delta, in case `SELFDESTRUCT` is bricked.
                    mstore(0x00, to)
                    pop(call(gas(), mover, amount, 0x00, 0x20, codesize(), 0x00))
                    // If `address(to).balance >= amount + balanceBefore`, skip vault workflow.
                    if iszero(lt(balance(to), add(amount, balanceBefore))) { break }
                    // Just in case `SELFDESTRUCT` is changed to not revert and do nothing.
                    if lt(selfBalanceBefore, selfbalance()) { invalid() }
                }
                let m := mload(0x40)
                // If the mover is missing or bricked, deploy a minimal vault
                // that withdraws all ETH to `to` when being called only by `to`.
                // forgefmt: disable-next-item
                mstore(add(m, 0x20), 0x33146025575b600160005260206000f35b3d3d3d3d47335af1601a5760003dfd)
                mstore(m, or(to, shl(160, 0x6035600b3d3960353df3fe73)))
                // Compute and store the bytecode hash.
                mstore8(0x00, 0xff) // Write the prefix.
                mstore(0x35, keccak256(m, 0x40))
                mstore(0x01, shl(96, address())) // Deployer.
                mstore(0x15, 0) // Salt.
                vault := keccak256(0x00, 0x55)
                pop(call(gas(), vault, amount, codesize(), 0x00, codesize(), 0x00))
                // The vault returns a single word on success. Failure reverts with empty data.
                if iszero(returndatasize()) {
                    if iszero(create2(0, m, 0x40, 0)) { revert(codesize(), codesize()) } // For gas estimation.
                }
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Performs a `token.balanceOf(account)` check.
    /// `implemented` denotes whether the `token` does not implement `balanceOf`.
    /// `amount` is zero if the `token` does not implement `balanceOf`.
    function checkBalanceOf(address token, address account)
        internal
        view
        returns (bool implemented, uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            implemented :=
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                )
            amount := mul(mload(0x20), implemented)
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
    function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let addressMask := shr(96, not(0))
            let m := mload(0x40)
            mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
            mstore(add(m, 0x20), and(addressMask, token))
            mstore(add(m, 0x40), and(addressMask, spender))
            mstore(add(m, 0x60), and(addressMask, amount))
            mstore(add(m, 0x80), and(0xffffffffffff, expiration))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Revokes an approval for `token` and `spender` for `address(this)`.
    function permit2Lockdown(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0xcc53287f) // `Permit2.lockdown`.
            mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
            mstore(add(m, 0x40), 1) // `approvals.length`.
            mstore(add(m, 0x60), shr(96, shl(96, token)))
            mstore(add(m, 0x80), shr(96, shl(96, spender)))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}
          

/EfficientHashLib.sol

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

/// @notice Library for efficiently performing keccak256 hashes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EfficientHashLib.sol)
/// @dev To avoid stack-too-deep, you can use:
/// ```
/// bytes32[] memory buffer = EfficientHashLib.malloc(10);
/// EfficientHashLib.set(buffer, 0, value0);
/// ..
/// EfficientHashLib.set(buffer, 9, value9);
/// bytes32 finalHash = EfficientHashLib.hash(buffer);
/// ```
library EfficientHashLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               MALLOC-LESS HASHING OPERATIONS               */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `keccak256(abi.encode(v0))`.
    function hash(bytes32 v0) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            result := keccak256(0x00, 0x20)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0))`.
    function hash(uint256 v0) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            result := keccak256(0x00, 0x20)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1))`.
    function hash(bytes32 v0, bytes32 v1) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            mstore(0x20, v1)
            result := keccak256(0x00, 0x40)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1))`.
    function hash(uint256 v0, uint256 v1) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            mstore(0x20, v1)
            result := keccak256(0x00, 0x40)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            result := keccak256(m, 0x60)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
    function hash(uint256 v0, uint256 v1, uint256 v2) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            result := keccak256(m, 0x60)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            result := keccak256(m, 0x80)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            result := keccak256(m, 0x80)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            result := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            result := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4, bytes32 v5)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            result := keccak256(m, 0xc0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4, uint256 v5)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            result := keccak256(m, 0xc0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            result := keccak256(m, 0xe0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            result := keccak256(m, 0xe0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            result := keccak256(m, 0x100)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            result := keccak256(m, 0x100)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            result := keccak256(m, 0x120)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            result := keccak256(m, 0x120)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            result := keccak256(m, 0x140)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            result := keccak256(m, 0x140)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            result := keccak256(m, 0x160)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            result := keccak256(m, 0x160)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            result := keccak256(m, 0x180)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            result := keccak256(m, 0x180)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11,
        bytes32 v12
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            result := keccak256(m, 0x1a0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11,
        uint256 v12
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            result := keccak256(m, 0x1a0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11,
        bytes32 v12,
        bytes32 v13
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            mstore(add(m, 0x1a0), v13)
            result := keccak256(m, 0x1c0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11,
        uint256 v12,
        uint256 v13
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            mstore(add(m, 0x1a0), v13)
            result := keccak256(m, 0x1c0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*             BYTES32 BUFFER HASHING OPERATIONS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `keccak256(abi.encode(buffer[0], .., buffer[buffer.length - 1]))`.
    function hash(bytes32[] memory buffer) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := keccak256(add(buffer, 0x20), shl(5, mload(buffer)))
        }
    }

    /// @dev Sets `buffer[i]` to `value`, without a bounds check.
    /// Returns the `buffer` for function chaining.
    function set(bytes32[] memory buffer, uint256 i, bytes32 value)
        internal
        pure
        returns (bytes32[] memory)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(buffer, shl(5, add(1, i))), value)
        }
        return buffer;
    }

    /// @dev Sets `buffer[i]` to `value`, without a bounds check.
    /// Returns the `buffer` for function chaining.
    function set(bytes32[] memory buffer, uint256 i, uint256 value)
        internal
        pure
        returns (bytes32[] memory)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(buffer, shl(5, add(1, i))), value)
        }
        return buffer;
    }

    /// @dev Returns `new bytes32[](n)`, without zeroing out the memory.
    function malloc(uint256 n) internal pure returns (bytes32[] memory buffer) {
        /// @solidity memory-safe-assembly
        assembly {
            buffer := mload(0x40)
            mstore(buffer, n)
            mstore(0x40, add(shl(5, add(1, n)), buffer))
        }
    }

    /// @dev Frees memory that has been allocated for `buffer`.
    /// No-op if `buffer.length` is zero, or if new memory has been allocated after `buffer`.
    function free(bytes32[] memory buffer) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(buffer)
            mstore(shl(6, lt(iszero(n), eq(add(shl(5, add(1, n)), buffer), mload(0x40)))), buffer)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      EQUALITY CHECKS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a == abi.decode(b, (bytes32))`.
    function eq(bytes32 a, bytes memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(0x20, mload(b)), eq(a, mload(add(b, 0x20))))
        }
    }

    /// @dev Returns `abi.decode(a, (bytes32)) == a`.
    function eq(bytes memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(0x20, mload(a)), eq(b, mload(add(a, 0x20))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               BYTE SLICE HASHING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function hash(bytes memory b, uint256 start, uint256 end)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            end := xor(end, mul(xor(end, n), lt(n, end)))
            start := xor(start, mul(xor(start, n), lt(n, start)))
            result := keccak256(add(add(b, 0x20), start), mul(gt(end, start), sub(end, start)))
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
    function hash(bytes memory b, uint256 start) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            start := xor(start, mul(xor(start, n), lt(n, start)))
            result := keccak256(add(add(b, 0x20), start), mul(gt(n, start), sub(n, start)))
        }
    }

    /// @dev Returns the keccak256 of the bytes.
    function hash(bytes memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := keccak256(add(b, 0x20), mload(b))
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function hashCalldata(bytes calldata b, uint256 start, uint256 end)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(end, start), sub(end, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := keccak256(mload(0x40), n)
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
    function hashCalldata(bytes calldata b, uint256 start) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(b.length, start), sub(b.length, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := keccak256(mload(0x40), n)
        }
    }

    /// @dev Returns the keccak256 of the bytes.
    function hashCalldata(bytes calldata b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            calldatacopy(mload(0x40), b.offset, b.length)
            result := keccak256(mload(0x40), b.length)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      SHA2-256 HELPERS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `sha256(abi.encode(b))`. Yes, it's more efficient.
    function sha2(bytes32 b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, b)
            result := mload(staticcall(gas(), 2, 0x00, 0x20, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function sha2(bytes memory b, uint256 start, uint256 end)
        internal
        view
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            end := xor(end, mul(xor(end, n), lt(n, end)))
            start := xor(start, mul(xor(start, n), lt(n, start)))
            // forgefmt: disable-next-item
            result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
                mul(gt(end, start), sub(end, start)), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
    function sha2(bytes memory b, uint256 start) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            start := xor(start, mul(xor(start, n), lt(n, start)))
            // forgefmt: disable-next-item
            result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
                mul(gt(n, start), sub(n, start)), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the bytes.
    function sha2(bytes memory b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(staticcall(gas(), 2, add(b, 0x20), mload(b), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function sha2Calldata(bytes calldata b, uint256 start, uint256 end)
        internal
        view
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(end, start), sub(end, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
    function sha2Calldata(bytes calldata b, uint256 start) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(b.length, start), sub(b.length, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the bytes.
    function sha2Calldata(bytes calldata b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            calldatacopy(mload(0x40), b.offset, b.length)
            result := mload(staticcall(gas(), 2, mload(0x40), b.length, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }
}
          

/PreimageLocation.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {EfficientHashLib} from "solady/src/utils/EfficientHashLib.sol";

library PreimageLocation {
    struct Info {
        address provider;
        bool callAtChange;
        bool durationIsTimestamp;
        uint256 duration;
        address token;
        uint256 price;
        uint256 offset;
        uint256 index;
    }

    using PreimageLocation for Info;
    using PreimageLocation for bytes32;
    using EfficientHashLib for bytes32;

    /**
     * derive a unique location hash that is hash(section + index)
     * @param info location info to help derive hashes
     */
    function location(Info memory info) internal pure returns (bytes32) {
        return info.section().location(info.index);
    }

    function location(
        bytes32 sec,
        uint256 index
    ) internal pure returns (bytes32) {
        return sec.hash(bytes32(index));
    }

    function section(Info memory info) internal pure returns (bytes32) {
        unchecked {
            return
                EfficientHashLib.hash(
                    bytes32(uint256(uint160(info.provider))),
                    bytes32(info.encodeToken()),
                    bytes32(info.price),
                    bytes32(info.offset)
                );
        }
    }

    function encodeToken(Info memory info) internal pure returns (uint256) {
        return
            (uint256(info.durationIsTimestamp ? 1 : 0) << 255) |
            (uint256(info.callAtChange ? 1 : 0) << 254) |
            (uint256((uint40(info.duration) << 1) >> 1) << 160) |
            uint256(uint160(info.token));
    }
}
          

/IRandom.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

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

abstract contract IRandom {
    uint256 internal constant ZERO = 0;
    uint256 internal constant ONE = 1;
    uint256 internal constant TWO = 2;
    uint256 internal constant EIGHT = 8;
    uint256 internal constant ONE_SIX = 16;
    uint256 internal constant THREE_TWO = 32;
    uint256 internal constant FOUR_EIGHT = 48;
    uint256 internal constant NINE_SIX = 96;
    uint256 internal constant ONE_TWO_EIGHT = 128;
    uint256 internal constant ONE_SIX_ZERO = 160;
    uint256 internal constant TWO_ZERO_EIGHT = 208;
    uint256 internal constant TWO_ZERO_NINE = ONE + TWO_ZERO_EIGHT;
    uint256 internal constant TWO_FOUR_EIGHT = 248;
    uint256 internal constant TWO_FIVE_FIVE = 255;
    uint256 internal constant TWO_FIVE_SIX = 256;

    mapping(bytes32 key => uint256 timeline) internal _timeline;
    mapping(bytes32 key => bytes32 seed) internal _seed;

    struct Randomness {
        address owner;
        bool callAtChange;
        bool usesTimestamp;
        uint256 duration;
        uint256 start;
        uint256 timeline;
        uint256 contributed;
        bytes32 seed;
    }

    function heat(
        uint256 required,
        PreimageLocation.Info calldata settings,
        PreimageLocation.Info[] calldata info,
        bool useTSTORE
    ) external payable virtual returns (bytes32);

    function pointer(PreimageLocation.Info calldata info) external view virtual returns (address);

    function consumed(PreimageLocation.Info calldata info) external view virtual returns (bool);

    function randomness(bytes32 key) external view virtual returns (Randomness memory);

    function latest(address account, bool onlySameTx, bool useTSTORE) external view virtual returns (bytes32);

    function expired(uint256 timeline) external view virtual returns (bool) {
        return _expired(timeline);
    }

    function _expired(uint256 timeline) internal view virtual returns (bool) {
        unchecked {
            // end
            return
                (
                    (timeline << (TWO_FIVE_FIVE - (EIGHT + ONE))) >>
                        TWO_FIVE_FIVE ==
                        ZERO
                        ? block.number
                        : block.timestamp
                ) -
                    // start
                    (uint256(uint48(timeline >> FOUR_EIGHT))) >
                // expiration delta
                (uint256(uint40(timeline) >> (EIGHT + TWO)));
        }
    }
}
          

/ConsumerReceiver.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

abstract contract ConsumerReceiver {
    function onReverse(
        bytes32 /*key*/,
        address /*token*/,
        uint256 /*amount*/
    ) external virtual;

    function onCast(bytes32 /*key*/, bytes32 /*seed*/) external virtual;

    function onChop(bytes32 /*key*/) external virtual;
}
          

/GameBase.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {ConsumerReceiver} from "./implementations/ConsumerReceiver.sol";
import {IRandom} from "./implementations/IRandom.sol";
import {PreimageLocation} from "./PreimageLocation.sol";

/// @notice Shared base for the games platform. Holds everything CoinFlip and Raffle share and
/// nothing game-specific: the core Random reference, native-token escrow helpers, an owner-managed
/// validator allowlist (read through a swappable seam), the binding-plus-membership heat helper,
/// the onCast dispatch reverse index with its guards, and the timeout-recovery surface. The games
/// ink nothing and contribute nothing to the seed — entropy is validator-only and pinned.
abstract contract GameBase is ConsumerReceiver {
    using SafeTransferLib for address;

    error OnlyRandom();
    error OnlyOwner();
    error NotAllowlisted();
    error BadSubset();
    error SubsetMismatch();
    error StakeMismatch();

    event OwnerTransferred(address indexed previousOwner, address indexed newOwner);
    event ValidatorAdded(address indexed validator);
    event ValidatorRemoved(address indexed validator);

    /// @notice core Random.
    address public immutable random;
    /// @notice owner address controlling the validator allowlist and fees (a plain address in v1).
    address public owner;

    /// @notice the owner-managed allowlist (the validator "universe"). Read through _isAllowlisted
    /// so a future version can override to delegate to an external IValidatorRegistry.
    mapping(address validator => bool allowed) public isValidator;
    uint256 public validatorCount;

    /// @notice minimum distinct validators a game instance's declared subset must span. The
    /// safety floor: a subset with at least one honest validator defeats selection-grinding.
    uint256 public constant MIN_SUBSET = 3;

    /// @notice blocks after a draw is armed before its escrow becomes reclaimable if the seed
    /// never finalizes (the liveness timeout). Matches the prior CoinFlip constant.
    uint256 public constant STALE_BLOCKS = 200;

    /// @notice canonical heat settings: native token, price 0, fixed duration. The duration is the
    /// expiry window the cast must land within.
    bool internal constant DURATION_IS_TIMESTAMP = false;
    uint256 public constant HEAT_DURATION = 12;
    address internal constant HEAT_TOKEN = address(0);

    /// @notice reverse index from a Random request key to the game instance it settles.
    mapping(bytes32 key => bytes32 instanceId) public instanceByKey;
    /// @notice instances whose draw was chopped at expiry (seed never formed) — a liveness failure.
    mapping(bytes32 instanceId => bool chopped) public choppedInstance;

    constructor(address _random) {
        random = _random;
        owner = msg.sender;
        emit OwnerTransferred(address(0), msg.sender);
    }

    modifier onlyOwner() {
        if (msg.sender != owner) revert OnlyOwner();
        _;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        emit OwnerTransferred(owner, newOwner);
        owner = newOwner;
    }

    function addValidator(address validator) external onlyOwner {
        if (!isValidator[validator]) {
            isValidator[validator] = true;
            unchecked { ++validatorCount; }
            emit ValidatorAdded(validator);
        }
    }

    function removeValidator(address validator) external onlyOwner {
        if (isValidator[validator]) {
            isValidator[validator] = false;
            unchecked { --validatorCount; }
            emit ValidatorRemoved(validator);
        }
    }

    /// @notice The swappable membership seam. Defaults to the local owner-managed allowlist.
    function _isAllowlisted(address validator) internal view virtual returns (bool) {
        return isValidator[validator];
    }

    // --- native-token escrow ---

    /// @notice Assert the value sent equals the expected stake. Native escrow needs no pull: the
    /// value already arrived with the call. This validates at the boundary and fails fast.
    function _take(uint256 expected) internal view {
        if (msg.value != expected) revert StakeMismatch();
    }

    /// @notice Pay a winner.
    function _pay(address to, uint256 amount) internal {
        to.safeTransferETH(amount);
    }

    /// @notice Refund an escrowed stake.
    function _refund(address to, uint256 amount) internal {
        to.safeTransferETH(amount);
    }

    /// @notice Validate a declared subset at instance creation: at least MIN_SUBSET members, all
    /// distinct, all allowlisted. Distinctness is enforced here (once, cheaply); _heatBound's
    /// binding then guarantees the heated set equals this validated subset.
    function _validateSubset(address[] calldata subset) internal view {
        uint256 n = subset.length;
        if (n < MIN_SUBSET) revert BadSubset();
        for (uint256 i = 0; i < n; ++i) {
            address v = subset[i];
            if (!_isAllowlisted(v)) revert NotAllowlisted();
            for (uint256 j = i + 1; j < n; ++j) {
                if (subset[j] == v) revert BadSubset();
            }
        }
    }

    /// @notice Heat exactly the declared subset's preimages, with this contract as request owner and
    /// the change callback on (so Random calls onCast at finalization). Enforces:
    ///   binding — locations.length == subset.length (required == count, no slack) and each
    ///     location's provider equals the subset member at the same index (no sybil substitution);
    ///   membership — each subset member is still allowlisted at heat time (protects a raw-contract
    ///     caller who never touched the front end).
    /// Provider-level binding suffices: a subset containing one honest provider defeats grinding
    /// regardless of which of that provider's preimages is chosen, because the attacker never
    /// learns the honest secret.
    function _heatBound(address[] memory subset, PreimageLocation.Info[] calldata locations)
        internal
        returns (bytes32 key)
    {
        uint256 n = subset.length;
        if (locations.length != n) revert SubsetMismatch();
        for (uint256 i = 0; i < n; ++i) {
            if (locations[i].provider != subset[i]) revert SubsetMismatch();
            if (!_isAllowlisted(subset[i])) revert NotAllowlisted();
        }
        PreimageLocation.Info memory settings = PreimageLocation.Info({
            provider: address(this),
            callAtChange: true,
            durationIsTimestamp: DURATION_IS_TIMESTAMP,
            duration: HEAT_DURATION,
            token: HEAT_TOKEN,
            price: 0,
            offset: 0,
            index: 0
        });
        key = IRandom(random).heat(n, settings, locations, false);
    }

    // --- ConsumerReceiver callbacks and dispatch ---

    /// @notice Core Random calls this when a request's seed finalizes (callAtChange was set on
    /// heat). Looks up the instance by key and routes to the game's _settle.
    function onCast(bytes32 key, bytes32 seed) external override {
        if (msg.sender != random) revert OnlyRandom();
        _settle(instanceByKey[key], seed);
    }

    /// @notice Core Random calls this when a request is chopped at expiry (the seed never formed).
    /// Records the instance as a liveness failure so the game's refund path can fire.
    function onChop(bytes32 key) external override {
        if (msg.sender != random) revert OnlyRandom();
        bytes32 instanceId = instanceByKey[key];
        choppedInstance[instanceId] = true;
        _onChop(instanceId);
    }

    function onReverse(bytes32, address, uint256) external override {}

    /// @notice The game-specific settlement, invoked by onCast (push) and the game's pull fallback.
    function _settle(bytes32 instanceId, bytes32 seed) internal virtual;

    /// @notice Optional hook for a game to react to a chop beyond the recorded flag.
    function _onChop(bytes32 instanceId) internal virtual {}

    /// @notice True once `armedAtBlock + STALE_BLOCKS` has passed.
    function _isStale(uint256 armedAtBlock) internal view returns (bool) {
        return block.number >= armedAtBlock + STALE_BLOCKS;
    }
}
          

Compiler Settings

{"viaIR":true,"remappings":[],"optimizer":{"runs":1000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"shanghai","compilationTarget":{"contracts/Raffle.sol":"Raffle"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_random","internalType":"address"}]},{"type":"error","name":"AlreadyRevealed","inputs":[]},{"type":"error","name":"BadFee","inputs":[]},{"type":"error","name":"BadParams","inputs":[]},{"type":"error","name":"BadReveal","inputs":[]},{"type":"error","name":"BadSubset","inputs":[]},{"type":"error","name":"GuessOutOfRange","inputs":[]},{"type":"error","name":"NotAllowlisted","inputs":[]},{"type":"error","name":"NotFilling","inputs":[]},{"type":"error","name":"NotTicketOwner","inputs":[]},{"type":"error","name":"NothingToRefund","inputs":[]},{"type":"error","name":"OnlyOwner","inputs":[]},{"type":"error","name":"OnlyRandom","inputs":[]},{"type":"error","name":"PeriodNotElapsed","inputs":[]},{"type":"error","name":"StakeMismatch","inputs":[]},{"type":"error","name":"SubsetMismatch","inputs":[]},{"type":"error","name":"ThresholdNotMet","inputs":[]},{"type":"error","name":"TicketInactive","inputs":[]},{"type":"error","name":"TooEarly","inputs":[]},{"type":"error","name":"WindowClosed","inputs":[]},{"type":"error","name":"WindowOpen","inputs":[]},{"type":"error","name":"WrongRoundState","inputs":[]},{"type":"event","name":"Armed","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"key","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"Committed","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256","indexed":true},{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"address","name":"player","internalType":"address","indexed":true},{"type":"bytes32","name":"commitment","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"Drawn","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"uint256","name":"draw","internalType":"uint256","indexed":false},{"type":"uint256","name":"claimDeadline","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Finalised","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"address","name":"winner","internalType":"address","indexed":true},{"type":"uint256","name":"payout","internalType":"uint256","indexed":false},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NoContest","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"uint256","name":"potPerValidator","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnerTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Revealed","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256","indexed":true},{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"uint256","name":"guess","internalType":"uint256","indexed":false},{"type":"uint256","name":"distance","internalType":"uint256","indexed":false},{"type":"bool","name":"leading","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"RoundOpened","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32","indexed":true},{"type":"uint256","name":"stake","internalType":"uint256","indexed":false},{"type":"uint256","name":"threshold","internalType":"uint256","indexed":false},{"type":"uint256","name":"period","internalType":"uint256","indexed":false},{"type":"bytes32","name":"subsetHash","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"TicketCancelled","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"TicketRefunded","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"ValidatorAdded","inputs":[{"type":"address","name":"validator","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ValidatorRemoved","inputs":[{"type":"address","name":"validator","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BIPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"CLAIM_BLOCKS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"HEAT_DURATION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MIN_SUBSET","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"RANGE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"STALE_BLOCKS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"}],"name":"activeRound","inputs":[{"type":"bytes32","name":"tupleHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addValidator","inputs":[{"type":"address","name":"validator","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"arm","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"},{"type":"tuple[]","name":"validatorLocations","internalType":"struct PreimageLocation.Info[]","components":[{"type":"address","name":"provider","internalType":"address"},{"type":"bool","name":"callAtChange","internalType":"bool"},{"type":"bool","name":"durationIsTimestamp","internalType":"bool"},{"type":"uint256","name":"duration","internalType":"uint256"},{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"price","internalType":"uint256"},{"type":"uint256","name":"offset","internalType":"uint256"},{"type":"uint256","name":"index","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancel","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"chopped","internalType":"bool"}],"name":"choppedInstance","inputs":[{"type":"bytes32","name":"instanceId","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"ticketId","internalType":"uint256"}],"name":"commit","inputs":[{"type":"uint256","name":"stake","internalType":"uint256"},{"type":"uint256","name":"threshold","internalType":"uint256"},{"type":"uint256","name":"period","internalType":"uint256"},{"type":"address[]","name":"validatorSubset","internalType":"address[]"},{"type":"bytes32","name":"commitment","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feeBips","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeRecipient","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"finalise","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"instanceId","internalType":"bytes32"}],"name":"instanceByKey","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"allowed","internalType":"bool"}],"name":"isValidator","inputs":[{"type":"address","name":"validator","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nextTicket","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onCast","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32"},{"type":"bytes32","name":"seed","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onChop","inputs":[{"type":"bytes32","name":"key","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onReverse","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"random","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recordDraw","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"refundTicket","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeValidator","inputs":[{"type":"address","name":"validator","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reveal","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256"},{"type":"uint256","name":"guess","internalType":"uint256"},{"type":"bytes32","name":"salt","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"roundSubset","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"stake","internalType":"uint256"},{"type":"uint256","name":"threshold","internalType":"uint256"},{"type":"uint256","name":"period","internalType":"uint256"},{"type":"bytes32","name":"subsetHash","internalType":"bytes32"},{"type":"uint256","name":"createdAtBlock","internalType":"uint256"},{"type":"uint256","name":"commitCount","internalType":"uint256"},{"type":"uint256","name":"pot","internalType":"uint256"},{"type":"uint8","name":"status","internalType":"enum Raffle.Status"},{"type":"bytes32","name":"key","internalType":"bytes32"},{"type":"uint256","name":"armedAtBlock","internalType":"uint256"},{"type":"uint256","name":"draw","internalType":"uint256"},{"type":"uint256","name":"claimDeadline","internalType":"uint256"},{"type":"uint256","name":"bestTicket","internalType":"uint256"},{"type":"uint256","name":"bestDistance","internalType":"uint256"},{"type":"uint256","name":"settledPot","internalType":"uint256"}],"name":"rounds","inputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFee","inputs":[{"type":"uint256","name":"newFeeBips","internalType":"uint256"},{"type":"address","name":"newRecipient","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"roundId","internalType":"bytes32"},{"type":"address","name":"player","internalType":"address"},{"type":"bytes32","name":"commitment","internalType":"bytes32"},{"type":"uint256","name":"committedAtBlock","internalType":"uint256"},{"type":"bool","name":"active","internalType":"bool"},{"type":"bool","name":"revealed","internalType":"bool"}],"name":"tickets","inputs":[{"type":"uint256","name":"ticketId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validatorCount","inputs":[]}]
              

Contract Creation Code

0x60a03460cc57601f61208a38819003918201601f19168301916001600160401b0383118484101760d05780849260209460405283398101031260cc57516001600160a01b038116810360cc576080525f8054336001600160a01b031991821681178355604051927f8934ce4adea8d9ce0d714d2c22b86790e41b7731c84b926fbbdc1d40ff6533c98180a333906006541617600655611fa590816100e5823960805181818161081d01528181610b7f01528181610d56015281816110970152818161124e015261140b0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f3560e01c806308dff88114611ae55780630e4ee5e114611acc5780630f43a67714611aaf5780631e8971a51461144b5780631f27220b146113f057806340a141ff1461137657806340e58ee5146112b3578063467be96f146111ec57806346904840146111c65780634d238c8e1461114757806350b44712146110d65780635d752c91146110bb5780635ec01e4d1461107857806361d49ea814610deb578063621f85f914610dcf57806368ac311514610d3b5780636c1202a014610d0c5780636fba74c914610cf15780637577410014610aec57806385db75941461058f578063870489861461056557806387ee2ee11461053b5780638da5cb5b14610516578063a23ad88d14610429578063b4f2e8b8146103a1578063c66db92014610386578063c6b12826146102d4578063c7dbbc47146102b7578063d22290a41461029a578063dc593a791461027f578063f2fde38b146101e1578063f7050031146101c55763facd743b14610184575f80fd5b346101c15760203660031901126101c1576001600160a01b036101a5611b17565b165f526001602052602060ff60405f2054166040519015158152f35b5f80fd5b346101c1575f3660031901126101c15760206040516127108152f35b346101c15760203660031901126101c1576101fa611b17565b5f54906001600160a01b0391828116928333036102555773ffffffffffffffffffffffffffffffffffffffff19921680937f8934ce4adea8d9ce0d714d2c22b86790e41b7731c84b926fbbdc1d40ff6533c95f80a316175f55005b60046040517f5fc483c5000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c1576020604051600c8152f35b346101c1575f3660031901126101c1576020600554604051908152f35b346101c1575f3660031901126101c1576020600a54604051908152f35b346101c1576020806003193601126101c1576004355f526008815260405f20604051908183825491828152019081925f52845f20905f5b8682821061036957868661032182880383611ddd565b60405192839281840190828552518091526040840192915f5b82811061034957505050500390f35b83516001600160a01b03168552869550938101939281019260010161033a565b83546001600160a01b03168552909301926001928301920161030b565b346101c1575f3660031901126101c157602060405160038152f35b346101c15760403660031901126101c1576004356103bd611b01565b6001600160a01b0391825f541633036102555761271081116103ff576005551673ffffffffffffffffffffffffffffffffffffffff1960065416176006555f80f35b60046040517f917f1a53000000000000000000000000000000000000000000000000000000008152fd5b346101c15760203660031901126101c1576004355f52600760205260405f208054600182015460028301549260038101549360048201549460058301549560068401549660ff60078601541697600886015490600987015492600a88015494600b89015496600c8a015498600e600d8c01549b01549b60206040519e8f908152015260408d015260608c015260808b015260a08a015260c08901526006891015610502576101e09860e08901526101008801526101208701526101408601526101608501526101808401526101a08301526101c0820152f35b634e487b7160e01b5f52602160045260245ffd5b346101c1575f3660031901126101c15760206001600160a01b035f5416604051908152f35b346101c15760203660031901126101c1576004355f526003602052602060405f2054604051908152f35b346101c15760203660031901126101c1576004355f52600b602052602060405f2054604051908152f35b346101c15760403660031901126101c15767ffffffffffffffff80602435116101c1573660236024350112156101c15760243560040135116101c15736602480356004013560081b81350101116101c1576004355f52600760205260405f2060078101805460ff8116600681101561050257600103610ac2576005830154916001840154809310610a9857600484015491600285015492838101809111610a84574310610a5a5760029060ff19161790554360098401556006830154600e84015561069483549261068660038601546040519485936020850197889094939260609260808301968352602083015260408201520152565b03601f198101835282611ddd565b5190205f52600b60205260405f20600435815414610a51575b506004355f52600860205260405f2090604051808360208295549384815201905f5260205f20925f5b818110610a2f5750506106eb92500383611ddd565b8151918260243560040135036109f1575f5b8381106109615750506040519161071383611da4565b308352600160208401525f6040840152600c60608401525f60808401525f60a08401525f60c08401525f60e084015260e0604051937f7f5299bb00000000000000000000000000000000000000000000000000000000855261016485019260048601526001600160a01b038151166024860152602081015115156044860152604081015115156064860152606081015160848601526001600160a01b0360808201511660a486015260a081015160c486015260c081015160e4860152015161010484015261016061012484015260243560040135905261018482018260248035015f5b6024356004013581106108cd575050806020925f61014483015203815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af19182156108c2575f9261088d575b50600882910155805f52600360205260043560405f20556004357fc836cc7135658b985b0027f421041bb63e201b1245d42791936dba6f2868ede75f80a3005b91506020823d6020116108ba575b816108a860209383611ddd565b810103126101c157600891519161084d565b3d915061089b565b6040513d5f823e3d90fd5b9150916001906001600160a01b036108e485611b2d565b1681526108f360208501611f62565b1515602082015261090660408501611f62565b15156040820152606084013560608201526001600160a01b0361092b60808601611b2d565b16608082015260a084013560a082015260c084013560c082015260e084013560e0820152610100809101930191019084916107f6565b60243560040135811015610a1b5761098160248260081b81350101611e0d565b6001600160a01b039081806109968587611f4e565b51169116036109f1576109a98284611f4e565b51165f52600160205260ff60405f205416156109c7576001016106fd565b60046040517f06fb10a9000000000000000000000000000000000000000000000000000000008152fd5b60046040517f0e9b36de000000000000000000000000000000000000000000000000000000008152fd5b634e487b7160e01b5f52603260045260245ffd5b84546001600160a01b03168352600194850194879450602090930192016106d6565b5f9055816106ad565b60046040517ff0aaee65000000000000000000000000000000000000000000000000000000008152fd5b634e487b7160e01b5f52601160045260245ffd5b60046040517f59fa4a93000000000000000000000000000000000000000000000000000000008152fd5b60046040517ff381ae23000000000000000000000000000000000000000000000000000000008152fd5b346101c15760203660031901126101c157600435805f52600960205260405f206001600160a01b038060018301541691338303610cc757600481019182549160ff831615610c9d5754805f52600760205260405f209160ff600784015416600681101561050257600203610c8c576008830154604051918291630cf9dff760e01b835260048301528160246101009485937f0000000000000000000000000000000000000000000000000000000000000000165afa9081156108c25760e0925f92610c5f575b50500151610c1d575f52600460205260ff60405f2054161580610c47575b610c1d575f948593849360ff191690557f84c670532dd70e48d686000c31d3f098bbff814baed8f5aeb8f96f4bebf5e0488380a25438935af115610c1057005b63b12d13eb5f526004601cfd5b60046040517f085de625000000000000000000000000000000000000000000000000000000008152fd5b50600981015460c88101809111610a84574310610bd0565b610c7e9250803d10610c85575b610c768183611ddd565b810190611e2e565b8880610bb2565b503d610c6c565b6004604051633d9ab53d60e01b8152fd5b60046040517f4b1552d5000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe18d39ad000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c157602060405160648152f35b346101c15760203660031901126101c1576004355f526004602052602060ff60405f2054166040519015158152f35b346101c15760203660031901126101c1576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610da5576004355f52600360205260405f20545f52600460205260405f20600160ff198254161790555f80f35b60046040517fe9294e3f000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c15760206040516101008152f35b346101c15760603660031901126101c15760043560243590805f52600960205260405f2080545f52600760205260405f209260ff600785015416600681101561050257600303610c8c57600b840154431161104e576004820190815460ff811615610c9d5760ff8160081c1661102457600182108015611019575b610fef576040516020810190838252604435604082015233606082015260608152610e9081611dc1565b519020600285015403610fc5577f4f8bf1c2786b12c69053e2b19de082483e252ab56851fcbadd60574a8a748bb793606093610100879361ff001916179055600a870154838181115f14610fb55790610ee891611b41565b80975b5f90600c810191825480155f14610f32575050600194855b610f23575b505050505495604051928352602083015215156040820152a3005b600d9255015586888088610f08565b600d83015480861015610f4b5750505060019485610f03565b8597929714610f5c575b5085610f03565b805f97929752600960205260405f20600380880154910154908181105f14610f8b575050505060019485610f55565b979297149081610fab575b50610fa2575b85610f55565b60019550610f9c565b905081108c610f96565b610fbe91611b41565b8097610eeb565b60046040517f8ff14e0d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3eea16e7000000000000000000000000000000000000000000000000000000008152fd5b506101008211610e66565b60046040517fa89ac151000000000000000000000000000000000000000000000000000000008152fd5b60046040517f2f23cfcd000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c15760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101c1575f3660031901126101c157602060405160c88152f35b346101c15760203660031901126101c1576004355f52600960205260c060405f2060ff8154916001600160a01b0360018201541690600281015460046003830154920154926040519586526020860152604085015260608401528181161515608084015260081c16151560a0820152f35b346101c15760203660031901126101c157611160611b17565b6001600160a01b0390815f541633036102555716805f52600160205260405f20805460ff81161561118d57005b60019060ff19161790556001600254016002557fe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec38849875f80a2005b346101c1575f3660031901126101c15760206001600160a01b0360065416604051908152f35b346101c15760203660031901126101c157600435805f52600760205260405f2060ff600782015416600681101561050257600203610c8c576008015460405190630cf9dff760e01b825260048201526101009081816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156108c25760e0925f92611296575b505001518015610c1d5761129491611ec3565b005b6112ac9250803d10610c8557610c768183611ddd565b8380611281565b346101c15760203660031901126101c157600435805f52600960205260405f2060018101906001600160a01b0390818354163303610cc757600481019182549160ff831615610c9d57545f52600760205260405f209360ff600786015416600681101561050257600103610c8c575f958694859460ff1916905560058601841981540190556006865496018681540390557f90a5808be5f77b4a2e0aa0389f433983252aabf738204c5c421fc59e5ecdaeea8480a254169238935af115610c1057005b346101c15760203660031901126101c15761138f611b17565b6001600160a01b0390815f541633036102555716805f52600160205260405f20805460ff81166113bb57005b60ff19169055600280545f190190557fe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f15f80a2005b346101c15760403660031901126101c1576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610da5576004355f52600360205261129460243560405f2054611ec3565b60a03660031901126101c15760643567ffffffffffffffff81116101c157366023820112156101c15767ffffffffffffffff8160040135116101c157366024826004013560051b830101116101c157600435158015611aa5575b8015611a9b575b611a71576004353403611a47576003816004013510611a1d575f5b8160040135811061199657506040519060408201602080840152816004013590528160608101602483015f5b8460040135811061196a5750500391611514601f1993848101835282611ddd565b602081519101209160405161155e60208201928261155287604435602435600435899094939260609260808301968352602083015260408201520152565b03908101835282611ddd565b51902091825f52600b60205260405f20549283158015611944575b6116b6575b505050805f52600760205260405f2090611599600a54611dff565b9081600a556040519260c084019084821067ffffffffffffffff8311176116a2576020946006926040528381528581013381526004604083019260843584526060810143815260808201946001865260a08301945f86528a5f5260098c5260405f20935184556001600160a01b036001850191511673ffffffffffffffffffffffffffffffffffffffff1982541617905551600283015551600382015501915115159060ff61ff0084549251151560081b1692169061ffff1916171790556005810160018154019055016004358154019055604051906084358252827f14b2ba06b6ebabbf47943a3616deccae45265c0a7ee200161f557404b5ac6e11853394a4604051908152f35b634e487b7160e01b5f52604160045260245ffd5b909192506116c5600c54611dff565b80600c5560405160208101913083526040820152826060820152606081526116ec81611dc1565b51902092604051806101e081011067ffffffffffffffff6101e0830111176116a2576101e081016040526004358152602435602082015260443560408201528360608201524360808201525f60a08201525f60c0820152600160e08201525f6101008201525f6101208201525f6101408201525f6101608201525f6101808201525f6101a08201525f6101c0820152845f52600760205260405f2090805182556020810151600183015560408101516002830155606081015160038301556080810151600483015560a0810151600583015560c0810151600683015560e0810151600681101561050257600e916101c09160ff8019600787015416911617600785015561010081015160088501556101208101516009850155610140810151600a850155610160810151600b850155610180810151600c8501556101a0810151600d8501550151910155835f52600860205260405f20680100000000000000008260040135116116a257805460048301358083558111611919575b50908493929160248201905f5260205f205f5b836004013581106118f357505050507f23ff71f10a4811aac80ce7df19d9597b7590e39e15fff7ad614948b338ec36a2916118e8915f52600b6020528360405f2055604051918291604435602435600435859094939260609260808301968352602083015260408201520152565b0390a281808061157e565b829394959650602061190760019394611e0d565b8483015588979695940192910161187a565b815f5260205f2090836004013582015b8183018110611939575050611867565b5f8155600101611929565b50835f52600760205260ff600760405f2001541660068110156105025760011415611579565b91819350602080916001600160a01b03611985600195611b2d565b1681520193019101908492916114f3565b6001600160a01b03806119b96119b484866004013560248801611eb3565b611e0d565b16805f526001918260205260ff60405f205416156109c757828401808511610a84575b856004013581106119f357505050506001016114c7565b8282611a0d6119b4848a60049b999b013560248a01611eb3565b1614611a1d5785019492946119dc565b60046040517fb91181b2000000000000000000000000000000000000000000000000000000008152fd5b60046040517f4581e827000000000000000000000000000000000000000000000000000000008152fd5b60046040517f019af637000000000000000000000000000000000000000000000000000000008152fd5b50604435156114ac565b50602435156114a5565b346101c1575f3660031901126101c1576020600254604051908152f35b346101c15760603660031901126101c157611294611b01565b346101c15760203660031901126101c157611294600435611b61565b602435906001600160a01b03821682036101c157565b600435906001600160a01b03821682036101c157565b35906001600160a01b03821682036101c157565b91908203918211610a8457565b81810292918115918404141715610a8457565b805f52602060078152604090815f20600781019384549460ff8616600681101561050257600303611d9457600b830154431115611d6b576004809660ff1916179055600c600e830154920192835415611c6757612710611bc360055485611b4e565b0493545f52600981526001600160a01b03917f6601d3a54699023687da728a68a14bf9e9246dad405cad2c4bca46dcbfd010d1836001885f2001541696879387611c0d8189611b41565b918351928352820152a382611c43575b505f611c2c8194938293611b41565b38935af115611c385750565b63b12d13eb5f52601cfd5b5f906006541681808538935af115611c5b575f611c1d565b8363b12d13eb5f52601cfd5b925092835f9695965260088352805f208054928315611d5857838104935f5b818110611cbd57505050507f89d7f9ec8a91c69fe02357d1db44d4a3039e3b4cfcbd87f589d8932dbfc5abd19394955051908152a2565b8354811015611d4557835f526001600160a01b0381885f2001541660018201808311611d28578303611d3b575f198301838111611d2857905f80611d0b611d0582958c611b4e565b88611b41565b38935af115611d1c57600101611c86565b8963b12d13eb5f52601cfd5b60118c634e487b7160e01b5f525260245ffd5b5f90818089611d0b565b60328a634e487b7160e01b5f525260245ffd5b601288634e487b7160e01b5f525260245ffd5b600485517fa4229fab000000000000000000000000000000000000000000000000000000008152fd5b60048551633d9ab53d60e01b8152fd5b610100810190811067ffffffffffffffff8211176116a257604052565b6080810190811067ffffffffffffffff8211176116a257604052565b90601f8019910116810190811067ffffffffffffffff8211176116a257604052565b5f198114610a845760010190565b356001600160a01b03811681036101c15790565b519081151582036101c157565b90816101009103126101c15760405190611e4782611da4565b8051906001600160a01b03821682036101c15760e0918352611e6b60208201611e21565b6020840152611e7c60408201611e21565b6040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c0840152015160e082015290565b9190811015610a1b5760051b0190565b90815f52600760205260405f2090600782019081549160ff8316600681101561050257600203610c8c57600360ff93841916179055166001019081600111610a845781600a8201556064430191824311610a8457827f895f224c41f48a7b4eee5ba38e0f93cbbb08c745aee937ea0febf02a2c4bb12d93600b604094015582519182526020820152a2565b8051821015610a1b5760209160051b010190565b359081151582036101c15756fea2646970667358221220fd3ed60b4c5d2b9b83764316e4413c1024414d7f1e268e2cb45a0e38594f25f764736f6c6343000819003300000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61

Deployed ByteCode

0x60806040526004361015610011575f80fd5b5f3560e01c806308dff88114611ae55780630e4ee5e114611acc5780630f43a67714611aaf5780631e8971a51461144b5780631f27220b146113f057806340a141ff1461137657806340e58ee5146112b3578063467be96f146111ec57806346904840146111c65780634d238c8e1461114757806350b44712146110d65780635d752c91146110bb5780635ec01e4d1461107857806361d49ea814610deb578063621f85f914610dcf57806368ac311514610d3b5780636c1202a014610d0c5780636fba74c914610cf15780637577410014610aec57806385db75941461058f578063870489861461056557806387ee2ee11461053b5780638da5cb5b14610516578063a23ad88d14610429578063b4f2e8b8146103a1578063c66db92014610386578063c6b12826146102d4578063c7dbbc47146102b7578063d22290a41461029a578063dc593a791461027f578063f2fde38b146101e1578063f7050031146101c55763facd743b14610184575f80fd5b346101c15760203660031901126101c1576001600160a01b036101a5611b17565b165f526001602052602060ff60405f2054166040519015158152f35b5f80fd5b346101c1575f3660031901126101c15760206040516127108152f35b346101c15760203660031901126101c1576101fa611b17565b5f54906001600160a01b0391828116928333036102555773ffffffffffffffffffffffffffffffffffffffff19921680937f8934ce4adea8d9ce0d714d2c22b86790e41b7731c84b926fbbdc1d40ff6533c95f80a316175f55005b60046040517f5fc483c5000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c1576020604051600c8152f35b346101c1575f3660031901126101c1576020600554604051908152f35b346101c1575f3660031901126101c1576020600a54604051908152f35b346101c1576020806003193601126101c1576004355f526008815260405f20604051908183825491828152019081925f52845f20905f5b8682821061036957868661032182880383611ddd565b60405192839281840190828552518091526040840192915f5b82811061034957505050500390f35b83516001600160a01b03168552869550938101939281019260010161033a565b83546001600160a01b03168552909301926001928301920161030b565b346101c1575f3660031901126101c157602060405160038152f35b346101c15760403660031901126101c1576004356103bd611b01565b6001600160a01b0391825f541633036102555761271081116103ff576005551673ffffffffffffffffffffffffffffffffffffffff1960065416176006555f80f35b60046040517f917f1a53000000000000000000000000000000000000000000000000000000008152fd5b346101c15760203660031901126101c1576004355f52600760205260405f208054600182015460028301549260038101549360048201549460058301549560068401549660ff60078601541697600886015490600987015492600a88015494600b89015496600c8a015498600e600d8c01549b01549b60206040519e8f908152015260408d015260608c015260808b015260a08a015260c08901526006891015610502576101e09860e08901526101008801526101208701526101408601526101608501526101808401526101a08301526101c0820152f35b634e487b7160e01b5f52602160045260245ffd5b346101c1575f3660031901126101c15760206001600160a01b035f5416604051908152f35b346101c15760203660031901126101c1576004355f526003602052602060405f2054604051908152f35b346101c15760203660031901126101c1576004355f52600b602052602060405f2054604051908152f35b346101c15760403660031901126101c15767ffffffffffffffff80602435116101c1573660236024350112156101c15760243560040135116101c15736602480356004013560081b81350101116101c1576004355f52600760205260405f2060078101805460ff8116600681101561050257600103610ac2576005830154916001840154809310610a9857600484015491600285015492838101809111610a84574310610a5a5760029060ff19161790554360098401556006830154600e84015561069483549261068660038601546040519485936020850197889094939260609260808301968352602083015260408201520152565b03601f198101835282611ddd565b5190205f52600b60205260405f20600435815414610a51575b506004355f52600860205260405f2090604051808360208295549384815201905f5260205f20925f5b818110610a2f5750506106eb92500383611ddd565b8151918260243560040135036109f1575f5b8381106109615750506040519161071383611da4565b308352600160208401525f6040840152600c60608401525f60808401525f60a08401525f60c08401525f60e084015260e0604051937f7f5299bb00000000000000000000000000000000000000000000000000000000855261016485019260048601526001600160a01b038151166024860152602081015115156044860152604081015115156064860152606081015160848601526001600160a01b0360808201511660a486015260a081015160c486015260c081015160e4860152015161010484015261016061012484015260243560040135905261018482018260248035015f5b6024356004013581106108cd575050806020925f61014483015203815f6001600160a01b037f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61165af19182156108c2575f9261088d575b50600882910155805f52600360205260043560405f20556004357fc836cc7135658b985b0027f421041bb63e201b1245d42791936dba6f2868ede75f80a3005b91506020823d6020116108ba575b816108a860209383611ddd565b810103126101c157600891519161084d565b3d915061089b565b6040513d5f823e3d90fd5b9150916001906001600160a01b036108e485611b2d565b1681526108f360208501611f62565b1515602082015261090660408501611f62565b15156040820152606084013560608201526001600160a01b0361092b60808601611b2d565b16608082015260a084013560a082015260c084013560c082015260e084013560e0820152610100809101930191019084916107f6565b60243560040135811015610a1b5761098160248260081b81350101611e0d565b6001600160a01b039081806109968587611f4e565b51169116036109f1576109a98284611f4e565b51165f52600160205260ff60405f205416156109c7576001016106fd565b60046040517f06fb10a9000000000000000000000000000000000000000000000000000000008152fd5b60046040517f0e9b36de000000000000000000000000000000000000000000000000000000008152fd5b634e487b7160e01b5f52603260045260245ffd5b84546001600160a01b03168352600194850194879450602090930192016106d6565b5f9055816106ad565b60046040517ff0aaee65000000000000000000000000000000000000000000000000000000008152fd5b634e487b7160e01b5f52601160045260245ffd5b60046040517f59fa4a93000000000000000000000000000000000000000000000000000000008152fd5b60046040517ff381ae23000000000000000000000000000000000000000000000000000000008152fd5b346101c15760203660031901126101c157600435805f52600960205260405f206001600160a01b038060018301541691338303610cc757600481019182549160ff831615610c9d5754805f52600760205260405f209160ff600784015416600681101561050257600203610c8c576008830154604051918291630cf9dff760e01b835260048301528160246101009485937f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61165afa9081156108c25760e0925f92610c5f575b50500151610c1d575f52600460205260ff60405f2054161580610c47575b610c1d575f948593849360ff191690557f84c670532dd70e48d686000c31d3f098bbff814baed8f5aeb8f96f4bebf5e0488380a25438935af115610c1057005b63b12d13eb5f526004601cfd5b60046040517f085de625000000000000000000000000000000000000000000000000000000008152fd5b50600981015460c88101809111610a84574310610bd0565b610c7e9250803d10610c85575b610c768183611ddd565b810190611e2e565b8880610bb2565b503d610c6c565b6004604051633d9ab53d60e01b8152fd5b60046040517f4b1552d5000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe18d39ad000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c157602060405160648152f35b346101c15760203660031901126101c1576004355f526004602052602060ff60405f2054166040519015158152f35b346101c15760203660031901126101c1576001600160a01b037f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61163303610da5576004355f52600360205260405f20545f52600460205260405f20600160ff198254161790555f80f35b60046040517fe9294e3f000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c15760206040516101008152f35b346101c15760603660031901126101c15760043560243590805f52600960205260405f2080545f52600760205260405f209260ff600785015416600681101561050257600303610c8c57600b840154431161104e576004820190815460ff811615610c9d5760ff8160081c1661102457600182108015611019575b610fef576040516020810190838252604435604082015233606082015260608152610e9081611dc1565b519020600285015403610fc5577f4f8bf1c2786b12c69053e2b19de082483e252ab56851fcbadd60574a8a748bb793606093610100879361ff001916179055600a870154838181115f14610fb55790610ee891611b41565b80975b5f90600c810191825480155f14610f32575050600194855b610f23575b505050505495604051928352602083015215156040820152a3005b600d9255015586888088610f08565b600d83015480861015610f4b5750505060019485610f03565b8597929714610f5c575b5085610f03565b805f97929752600960205260405f20600380880154910154908181105f14610f8b575050505060019485610f55565b979297149081610fab575b50610fa2575b85610f55565b60019550610f9c565b905081108c610f96565b610fbe91611b41565b8097610eeb565b60046040517f8ff14e0d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3eea16e7000000000000000000000000000000000000000000000000000000008152fd5b506101008211610e66565b60046040517fa89ac151000000000000000000000000000000000000000000000000000000008152fd5b60046040517f2f23cfcd000000000000000000000000000000000000000000000000000000008152fd5b346101c1575f3660031901126101c15760206040516001600160a01b037f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61168152f35b346101c1575f3660031901126101c157602060405160c88152f35b346101c15760203660031901126101c1576004355f52600960205260c060405f2060ff8154916001600160a01b0360018201541690600281015460046003830154920154926040519586526020860152604085015260608401528181161515608084015260081c16151560a0820152f35b346101c15760203660031901126101c157611160611b17565b6001600160a01b0390815f541633036102555716805f52600160205260405f20805460ff81161561118d57005b60019060ff19161790556001600254016002557fe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec38849875f80a2005b346101c1575f3660031901126101c15760206001600160a01b0360065416604051908152f35b346101c15760203660031901126101c157600435805f52600760205260405f2060ff600782015416600681101561050257600203610c8c576008015460405190630cf9dff760e01b825260048201526101009081816024816001600160a01b037f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61165afa9081156108c25760e0925f92611296575b505001518015610c1d5761129491611ec3565b005b6112ac9250803d10610c8557610c768183611ddd565b8380611281565b346101c15760203660031901126101c157600435805f52600960205260405f2060018101906001600160a01b0390818354163303610cc757600481019182549160ff831615610c9d57545f52600760205260405f209360ff600786015416600681101561050257600103610c8c575f958694859460ff1916905560058601841981540190556006865496018681540390557f90a5808be5f77b4a2e0aa0389f433983252aabf738204c5c421fc59e5ecdaeea8480a254169238935af115610c1057005b346101c15760203660031901126101c15761138f611b17565b6001600160a01b0390815f541633036102555716805f52600160205260405f20805460ff81166113bb57005b60ff19169055600280545f190190557fe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f15f80a2005b346101c15760403660031901126101c1576001600160a01b037f00000000000000000000000087fc31413534733a09df5dc5aa33b4dba1f64b61163303610da5576004355f52600360205261129460243560405f2054611ec3565b60a03660031901126101c15760643567ffffffffffffffff81116101c157366023820112156101c15767ffffffffffffffff8160040135116101c157366024826004013560051b830101116101c157600435158015611aa5575b8015611a9b575b611a71576004353403611a47576003816004013510611a1d575f5b8160040135811061199657506040519060408201602080840152816004013590528160608101602483015f5b8460040135811061196a5750500391611514601f1993848101835282611ddd565b602081519101209160405161155e60208201928261155287604435602435600435899094939260609260808301968352602083015260408201520152565b03908101835282611ddd565b51902091825f52600b60205260405f20549283158015611944575b6116b6575b505050805f52600760205260405f2090611599600a54611dff565b9081600a556040519260c084019084821067ffffffffffffffff8311176116a2576020946006926040528381528581013381526004604083019260843584526060810143815260808201946001865260a08301945f86528a5f5260098c5260405f20935184556001600160a01b036001850191511673ffffffffffffffffffffffffffffffffffffffff1982541617905551600283015551600382015501915115159060ff61ff0084549251151560081b1692169061ffff1916171790556005810160018154019055016004358154019055604051906084358252827f14b2ba06b6ebabbf47943a3616deccae45265c0a7ee200161f557404b5ac6e11853394a4604051908152f35b634e487b7160e01b5f52604160045260245ffd5b909192506116c5600c54611dff565b80600c5560405160208101913083526040820152826060820152606081526116ec81611dc1565b51902092604051806101e081011067ffffffffffffffff6101e0830111176116a2576101e081016040526004358152602435602082015260443560408201528360608201524360808201525f60a08201525f60c0820152600160e08201525f6101008201525f6101208201525f6101408201525f6101608201525f6101808201525f6101a08201525f6101c0820152845f52600760205260405f2090805182556020810151600183015560408101516002830155606081015160038301556080810151600483015560a0810151600583015560c0810151600683015560e0810151600681101561050257600e916101c09160ff8019600787015416911617600785015561010081015160088501556101208101516009850155610140810151600a850155610160810151600b850155610180810151600c8501556101a0810151600d8501550151910155835f52600860205260405f20680100000000000000008260040135116116a257805460048301358083558111611919575b50908493929160248201905f5260205f205f5b836004013581106118f357505050507f23ff71f10a4811aac80ce7df19d9597b7590e39e15fff7ad614948b338ec36a2916118e8915f52600b6020528360405f2055604051918291604435602435600435859094939260609260808301968352602083015260408201520152565b0390a281808061157e565b829394959650602061190760019394611e0d565b8483015588979695940192910161187a565b815f5260205f2090836004013582015b8183018110611939575050611867565b5f8155600101611929565b50835f52600760205260ff600760405f2001541660068110156105025760011415611579565b91819350602080916001600160a01b03611985600195611b2d565b1681520193019101908492916114f3565b6001600160a01b03806119b96119b484866004013560248801611eb3565b611e0d565b16805f526001918260205260ff60405f205416156109c757828401808511610a84575b856004013581106119f357505050506001016114c7565b8282611a0d6119b4848a60049b999b013560248a01611eb3565b1614611a1d5785019492946119dc565b60046040517fb91181b2000000000000000000000000000000000000000000000000000000008152fd5b60046040517f4581e827000000000000000000000000000000000000000000000000000000008152fd5b60046040517f019af637000000000000000000000000000000000000000000000000000000008152fd5b50604435156114ac565b50602435156114a5565b346101c1575f3660031901126101c1576020600254604051908152f35b346101c15760603660031901126101c157611294611b01565b346101c15760203660031901126101c157611294600435611b61565b602435906001600160a01b03821682036101c157565b600435906001600160a01b03821682036101c157565b35906001600160a01b03821682036101c157565b91908203918211610a8457565b81810292918115918404141715610a8457565b805f52602060078152604090815f20600781019384549460ff8616600681101561050257600303611d9457600b830154431115611d6b576004809660ff1916179055600c600e830154920192835415611c6757612710611bc360055485611b4e565b0493545f52600981526001600160a01b03917f6601d3a54699023687da728a68a14bf9e9246dad405cad2c4bca46dcbfd010d1836001885f2001541696879387611c0d8189611b41565b918351928352820152a382611c43575b505f611c2c8194938293611b41565b38935af115611c385750565b63b12d13eb5f52601cfd5b5f906006541681808538935af115611c5b575f611c1d565b8363b12d13eb5f52601cfd5b925092835f9695965260088352805f208054928315611d5857838104935f5b818110611cbd57505050507f89d7f9ec8a91c69fe02357d1db44d4a3039e3b4cfcbd87f589d8932dbfc5abd19394955051908152a2565b8354811015611d4557835f526001600160a01b0381885f2001541660018201808311611d28578303611d3b575f198301838111611d2857905f80611d0b611d0582958c611b4e565b88611b41565b38935af115611d1c57600101611c86565b8963b12d13eb5f52601cfd5b60118c634e487b7160e01b5f525260245ffd5b5f90818089611d0b565b60328a634e487b7160e01b5f525260245ffd5b601288634e487b7160e01b5f525260245ffd5b600485517fa4229fab000000000000000000000000000000000000000000000000000000008152fd5b60048551633d9ab53d60e01b8152fd5b610100810190811067ffffffffffffffff8211176116a257604052565b6080810190811067ffffffffffffffff8211176116a257604052565b90601f8019910116810190811067ffffffffffffffff8211176116a257604052565b5f198114610a845760010190565b356001600160a01b03811681036101c15790565b519081151582036101c157565b90816101009103126101c15760405190611e4782611da4565b8051906001600160a01b03821682036101c15760e0918352611e6b60208201611e21565b6020840152611e7c60408201611e21565b6040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c0840152015160e082015290565b9190811015610a1b5760051b0190565b90815f52600760205260405f2090600782019081549160ff8316600681101561050257600203610c8c57600360ff93841916179055166001019081600111610a845781600a8201556064430191824311610a8457827f895f224c41f48a7b4eee5ba38e0f93cbbb08c745aee937ea0febf02a2c4bb12d93600b604094015582519182526020820152a2565b8051821015610a1b5760209160051b010190565b359081151582036101c15756fea2646970667358221220fd3ed60b4c5d2b9b83764316e4413c1024414d7f1e268e2cb45a0e38594f25f764736f6c63430008190033