false
true

Contract Address Details

0x3aeFe5D33aC82B0c550a5b83681c313aD1B788bc

Contract Name
GLucky1V2
Creator
0x8687d8–47bc82 at 0x0f7112–202a7e
Balance
0
Tokens
Fetching tokens...
Transactions
4,115 Transactions
Transfers
4,329 Transfers
Gas Used
1,276,680,531
Last Balance Update
26359611
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 partially verified via Sourcify.
Contract name:
GLucky1V2




Optimization enabled
true
Compiler version
v0.8.19+commit.7dd6d404




Optimization runs
1000000
EVM Version
default




Verified at
2025-08-19T10:55:15.289074Z

Constructor Arguments

0x000000000000000000000000333d978efa83687f133db145e3c6e4a25a0ec32e000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd00000000000000000000000000000000000000000000d3c21bcecceda100000000000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c1f2e2cbef8ac2082b785a35cf819dea9fab7f7b0000000000000000000000000000000000000000000000000000000000001a0b

Arg [0] (address) : 0x333d978efa83687f133db145e3c6e4a25a0ec32e
Arg [1] (address) : 0xabccefb00528c9c792ac7c46997f0f6ee5dcdddd
Arg [2] (uint256) : 1000000000000000000000000
Arg [3] (uint256) : 10000000000000000000000
Arg [4] (uint256) : 2
Arg [5] (address) : 0xc1f2e2cbef8ac2082b785a35cf819dea9fab7f7b
Arg [6] (uint256) : 6667

              

contracts/factory/GLucky1V2.sol

Sol2uml
new
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/**
 * @title IERC20Burnable
 * @dev Extended ERC20 interface with burn functionality
 */
interface IERC20Burnable is IERC20 {
    function burn(uint256 amount) external;
    function decimals() external view returns (uint8);
}

/**
 * @title GLucky1V2
 * @author GOODOG
 * @dev lottery game(commit-reveal pattern, RNG) with EIP-712 signatures, deadline validation
 * 
 * Core Features:
 * - Single Submission: Choose multiple numbers within the same numberRange
 * - Flexible Betting: Each number can have different bet amounts
 * - Unified Draw: Use the same random number to generate winning number
 * - Gas Optimization: Complete multiple number betting in one transaction
 *
 */
contract GLucky1V2 is Ownable, Pausable, ReentrancyGuard {
    using ECDSA for bytes32;

    /// @dev Prize token contract for rewards and payments
    IERC20Burnable public immutable prizeToken;
    
    /// @dev Base point constant for percentage calculations (10000 = 100%)
    uint256 public constant BASE_POINT = 10000;
    
    // ==================== EIP-712 Constants ====================
    
    /// @dev EIP-712 Domain Separator
    bytes32 public immutable DOMAIN_SEPARATOR;
    
    /// @dev EIP-712 TypeHash for CommitTicket (includes all sub-structures)
    bytes32 public constant COMMIT_TICKET_TYPEHASH = keccak256(
        "CommitTicket(address user,uint8 numberRange,NumberChoice[] choices,uint256 salt,uint256 nonce,uint256 deadline)" "NumberChoice(uint256 ticketAmount,uint8 chosenNumber)"
    );
    
    /// @dev EIP-712 TypeHash for NumberChoice
    bytes32 public constant NUMBER_CHOICE_TYPEHASH = keccak256(
        "NumberChoice(uint256 ticketAmount,uint8 chosenNumber)"
    );
    
    // ==================== Configuration Parameters ====================
    
    /// @dev Maximum bet amount per single ticket
    uint256 public maxTicketAmount;
    
    /// @dev Minimum bet amount per single ticket
    uint256 public minTicketAmount;
    
    /// @dev Number of blocks to wait before reveal
    uint256 public futureBlockOffset;
    
    /// @dev Default signature deadline duration (seconds)
    uint256 public defaultDeadlineDuration = 120; // 2 minutes
    
    // ==================== Packed Configuration ====================
    
    /// @dev Trusted signer address for signature verification
    address public trustedSigner;
    /// @dev Minimum allowed number range
    uint8 public minNumberRange = 2;
    /// @dev Maximum allowed number range
    uint8 public maxNumberRange = 99;
    
    // ==================== Fee Configuration ====================
    
    /// @dev Percentage of bet amount to burn (500 = 5%)
    uint256 public burnFeePercent = 500;
    /// @dev Percentage of bet amount for protocol fee (500 = 5%)
    uint256 public protocolFeePercent = 500;
    /// @dev Address to receive protocol fees
    address public protocolFeeRecipient;
    
    // ==================== RTP Configuration ====================
    
    /// @dev Return to Player percentage in basis points (6667 = 66.67%)
    uint256 public rtpPercent = 6667;
    
    // ==================== Statistics Data ====================
    
    /// @dev Total number of tickets created
    uint256 public totalTicketCount;
    /// @dev Total number of commits made
    uint256 public totalCommitCount;
    /// @dev Total amount of rewards paid out
    uint256 public totalRewardsPaid;
    /// @dev Total amount of tokens burned
    uint256 public totalBurnedAmount;
    /// @dev Total protocol fees collected
    uint256 public totalProtocolFees;
    
    // ==================== Core Data Structures ====================
    
    /**
     * @dev Number choice structure for multiple numbers within the same range
     * @param ticketAmount The bet amount for this specific number
     * @param chosenNumber The selected number (1 to numberRange)
     */
    struct NumberChoice {
        uint256 ticketAmount;
        uint8 chosenNumber;
    }
    
    /**
     * @dev Simplified ticket information stored within commit
     * @param ticketAmount Bet amount for this ticket
     * @param rewardPaid Amount of reward paid out (0 if not won or insufficient funds)
     * @param chosenNumber Selected number for this ticket
     */
    struct TicketInfo {
        uint256 ticketAmount;
        uint256 rewardPaid;
        uint8 chosenNumber;
    }
    
    /**
     * @dev Commit batch information containing all ticket data
     * @param user Address of the user who made the commit
     * @param totalAmount Total bet amount for all tickets in this commit
     * @param commitBlock Block number when commit was made
     * @param salt Random salt used for this commit
     * @param totalWinnings Total winnings paid out for this commit
     * @param rtpPercent RTP percentage used for this commit (in basis points)
     * @param offsetUsed Future block offset used at commit time to prevent parameter drift
     * @param numberRange Number range used for all tickets in this commit
     * @param winningNumber Winning number for this commit batch (0 if not revealed)
     * @param revealed Whether this commit has been revealed
     * @param won Whether this commit won any rewards
     */
    struct CommitInfo {
        address user;
        uint256 totalAmount;
        uint256 commitBlock;
        uint256 salt;
        uint256 totalWinnings;
        uint256 rtpPercent;
        uint256 offsetUsed;
        uint8 numberRange;
        uint8 winningNumber;
        bool revealed;
        bool won;
    }
    
    // ==================== Storage Mappings ====================
    
    /// @dev Mapping from commit ID to commit information
    mapping(uint256 => CommitInfo) public commits;
    
    /// @dev Mapping from commit ID to array of ticket information
    mapping(uint256 => TicketInfo[]) public commitTickets;
    
    /// @dev Mapping from user address to array of their commit IDs
    mapping(address => uint256[]) public userCommits;
    
    /// @dev Mapping from user address to their used nonce for signature verification
    mapping(address => uint256) public usedNonce;
    
    // ==================== Event Definitions ====================
    
    /**
     * @dev Emitted when tickets are committed in a single transaction
     * @param commitId Unique identifier for this commit batch
     * @param user Address of the user making the commit
     * @param numberRange Number range used for all tickets in this commit
     * @param choiceCount Number of choices/tickets in this commit
     * @param totalAmount Total bet amount for all tickets
     * @param targetBlock Block number for future reveal
     * @param rtpPercent RTP percentage used for this commit
     * @param offsetUsed Future block offset used at commit time
     */
    event TicketCommitted(
        uint256 indexed commitId,
        address indexed user,
        uint8 numberRange,
        uint256 choiceCount,
        uint256 totalAmount,
        uint256 targetBlock,
        uint256 rtpPercent,
        uint256 offsetUsed
    );
    
    /**
     * @dev Emitted when a commit batch is revealed (all tickets use same winning number)
     * @param commitId ID of the revealed commit
     * @param user Address of the commit owner
     * @param numberRange Number range used for this commit
     * @param choiceCount Number of choices/tickets in this commit
     * @param totalAmount Total bet amount for all tickets
     * @param winningNumber The winning number for this batch
     * @param won Whether this commit won any rewards
     * @param totalWinnings Total amount paid out as winnings
     * @param rtpPercent RTP percentage used for this commit
     */
    event TicketRevealed(
        uint256 indexed commitId,
        address indexed user,
        uint8 numberRange,
        uint256 choiceCount,
        uint256 totalAmount,
        uint8 winningNumber,
        bool indexed won,
        uint256 totalWinnings,
        uint256 rtpPercent
    );
    
    /**
     * @dev Emitted when prize pool has insufficient funds to pay a reward
     * @param commitId ID of the commit containing the winning ticket
     * @param user Address of the ticket owner
     * @param ticketIndex Index of the winning ticket that couldn't be paid
     * @param requiredAmount Amount needed for the reward
     * @param availableAmount Amount available in the prize pool
     */
    event InsufficientPrizePool(
        uint256 indexed commitId,
        address indexed user,
        uint256 indexed ticketIndex,
        uint256 requiredAmount,
        uint256 availableAmount
    );
    
    /**
     * @dev Emitted when fees are processed for a commit
     * @param commitId ID of the commit
     * @param totalAmount Total bet amount
     * @param burnAmount Amount burned
     * @param protocolAmount Amount sent to protocol fee recipient
     */
    event FeesProcessed(
        uint256 indexed commitId,
        uint256 totalAmount,
        uint256 burnAmount,
        uint256 protocolAmount
    );
    
    /**
     * @dev Emitted when the jackpot/prize pool is funded
     * @param sender Address that funded the jackpot
     * @param amount Amount added to the jackpot
     * @param newBalance New total balance of the prize pool
     */
    event JackpotFunded(
        address indexed sender,
        uint256 amount,
        uint256 newBalance
    );
    
    /**
     * @dev Emitted when number ranges are updated by owner
     * @param oldMinRange Previous minimum number range
     * @param oldMaxRange Previous maximum number range
     * @param newMinRange New minimum number range
     * @param newMaxRange New maximum number range
     */
    event NumberRangesUpdated(
        uint8 oldMinRange,
        uint8 oldMaxRange,
        uint8 newMinRange,
        uint8 newMaxRange
    );
    
    /**
     * @dev Emitted when RTP percentage is updated by owner
     * @param oldRtpPercent Previous RTP percentage in basis points
     * @param newRtpPercent New RTP percentage in basis points
     */
    event RtpPercentUpdated(
        uint256 oldRtpPercent,
        uint256 newRtpPercent
    );
    
    /**
     * @dev Emitted when future block offset is updated by owner
     * @param oldOffset Previous block offset value
     * @param newOffset New block offset value
     */
    event FutureBlockOffsetUpdated(
        uint256 oldOffset,
        uint256 newOffset
    );
    
    /**
     * @dev Emitted when ticket amount limits are updated by owner
     * @param oldMinAmount Previous minimum ticket amount
     * @param oldMaxAmount Previous maximum ticket amount
     * @param newMinAmount New minimum ticket amount
     * @param newMaxAmount New maximum ticket amount
     */
    event TicketAmountLimitsUpdated(
        uint256 oldMinAmount,
        uint256 oldMaxAmount,
        uint256 newMinAmount,
        uint256 newMaxAmount
    );
    
    /**
     * @dev Emitted when default deadline duration is updated
     * @param oldDuration Previous default deadline duration
     * @param newDuration New default deadline duration
     */
    event DefaultDeadlineDurationUpdated(
        uint256 oldDuration,
        uint256 newDuration
    );
    
    // ==================== Constructor ====================
    
    /**
     * @dev Contract constructor
     * @param _signer Trusted signer address for signature verification
     * @param _prizeToken Address of the prize token contract
     * @param _maxTicketAmount Maximum bet amount per ticket
     * @param _minTicketAmount Minimum bet amount per ticket
     * @param _offset Number of blocks to wait before reveal (max 128)
     * @param _protocolFeeRecipient Address to receive protocol fees
     * @param _rtpPercent Initial RTP percentage in basis points (e.g., 8000 = 80%)
     */
    constructor(
        address _signer,
        address _prizeToken,
        uint256 _maxTicketAmount,
        uint256 _minTicketAmount,
        uint256 _offset,
        address _protocolFeeRecipient,
        uint256 _rtpPercent
    ) {
        require(_signer != address(0), "Invalid signer");
        require(_prizeToken != address(0), "Invalid token");
        require(_maxTicketAmount > 0, "Invalid max amount");
        require(_minTicketAmount > 0, "Invalid min amount");
        require(_minTicketAmount <= _maxTicketAmount, "Min amount must be <= max amount");
        require(_protocolFeeRecipient != address(0), "Invalid recipient");
        require(_offset > 0 && _offset < 256, "Invalid block offset");
        require(_rtpPercent > 0 && _rtpPercent <= BASE_POINT, "Invalid RTP percent");
        
        trustedSigner = _signer;
        prizeToken = IERC20Burnable(_prizeToken);
        maxTicketAmount = _maxTicketAmount;
        minTicketAmount = _minTicketAmount;
        futureBlockOffset = _offset;
        protocolFeeRecipient = _protocolFeeRecipient;
        rtpPercent = _rtpPercent;
        
        // Initialize EIP-712 Domain Separator
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256("GLucky1V2"),
            keccak256("2.0"),
            block.chainid,
            address(this)
        ));
    }

    /**
     * @dev Validates if the number choices are valid
     * @param numberRange The number range to validate against
     * @param choices Array of number choices to validate
     * 
     * @notice This function reverts with specific error messages if validation fails
     */
    function validateChoices(uint8 numberRange, NumberChoice[] calldata choices) public view {
        require(numberRange >= minNumberRange && numberRange <= maxNumberRange, "Invalid number range");
        require(choices.length > 0, "No choices provided");
        require(choices.length <= numberRange, "Choices exceed number range");
        
        _validateChoicesInner(numberRange, choices);
    }
    
    /**
     * @dev Internal function to validate choices - reduces stack depth
     * @param numberRange The number range to validate against
     * @param choices Array of number choices to validate
     */
    function _validateChoicesInner(uint8 numberRange, NumberChoice[] calldata choices) internal view {
        _validateChoiceAmounts(choices);
        _validateChoiceNumbers(numberRange, choices);
    }
    
    /**
     * @dev Validates choice amounts
     * @param choices Array of number choices to validate
     */
    function _validateChoiceAmounts(NumberChoice[] calldata choices) internal view {
        for (uint256 i = 0; i < choices.length; i++) {
            require(choices[i].ticketAmount >= minTicketAmount, "Amount below minimum");
            require(choices[i].ticketAmount <= maxTicketAmount, "Amount exceeds max");
        }
    }
    
    /**
     * @dev Validates choice numbers and duplicates
     * @param numberRange The number range to validate against
     * @param choices Array of number choices to validate
     */
    function _validateChoiceNumbers(uint8 numberRange, NumberChoice[] calldata choices) internal pure {
        bool[256] memory usedNumbers; // Support up to 255 numbers (max numberRange)
        
        for (uint256 i = 0; i < choices.length; i++) {
            uint8 chosenNumber = choices[i].chosenNumber;
            
            require(chosenNumber >= 1 && chosenNumber <= numberRange, "Number out of range");
            require(!usedNumbers[chosenNumber], "Duplicate number found");
            
            usedNumbers[chosenNumber] = true;
        }
    }

    // ==================== Core Functionality ====================
    
    /**
     * @dev Commits multiple number tickets within the same number range with EIP-712 signature
     * @param numberRange Number range (shared by all numbers)
     * @param choices Array of number choices (number and corresponding bet amount)
     * @param salt Random salt value for randomness
     * @param nonce User nonce for signature verification
     * @param deadline Signature expiry timestamp
     * @param signature EIP-712 signature for the entire commit
     * 
     * Important Restrictions:
     * - Number of choices cannot exceed numberRange (cannot select more than total available)
     * - Cannot select duplicate numbers
     * - Each number must be within 1 to numberRange range
     * - Signature must not be expired (deadline check)
     * 
     * @notice This function allows users to bet on multiple numbers in a single transaction
     *         with different bet amounts for each number, using secure EIP-712 signatures
     */
    function commitTicket(
        uint8 numberRange,
        NumberChoice[] calldata choices,
        uint256 salt,
        uint256 nonce,
        uint256 deadline,
        bytes calldata signature
    ) external whenNotPaused nonReentrant {
        
        validateChoices(numberRange, choices);
        
        // Verify EIP-712 signature with deadline
        _verifyCommitSignature(msg.sender, numberRange, choices, salt, nonce, deadline, signature);
        usedNonce[msg.sender] = nonce;
        
        // Create commit batch
        uint256 commitId = ++totalCommitCount;
        uint256 totalAmount = 0;
        
        // Calculate total amount (validation already done in validateChoices)
        for (uint256 i = 0; i < choices.length; i++) {
            totalAmount += choices[i].ticketAmount;
        }
        
        // Transfer total amount
        require(prizeToken.transferFrom(msg.sender, address(this), totalAmount), "Payment failed");
        
        // Store ticket information directly in commitTickets array
        for (uint256 i = 0; i < choices.length; i++) {
            commitTickets[commitId].push(TicketInfo({
                chosenNumber: choices[i].chosenNumber,
                ticketAmount: choices[i].ticketAmount,
                rewardPaid: 0
            }));
        }
        
        // Record commit information
        commits[commitId] = CommitInfo({
            user: msg.sender,
            numberRange: numberRange,
            totalAmount: totalAmount,
            commitBlock: block.number,
            salt: salt,
            winningNumber: 0,
            revealed: false,
            won: false,
            totalWinnings: 0,
            rtpPercent: rtpPercent,
            offsetUsed: futureBlockOffset  // Store current futureBlockOffset to prevent parameter drift
        });
        
        userCommits[msg.sender].push(commitId);
        totalTicketCount += choices.length;
        
        emit TicketCommitted(
            commitId,
            msg.sender,
            numberRange,
            choices.length,
            totalAmount,
            block.number + futureBlockOffset,
            rtpPercent,
            futureBlockOffset
        );
    }
    
    /**
     * @dev Reveals the entire commit batch (all tickets use the same winning number)
     * @param commitId ID of the commit batch to reveal
     * 
     * @notice This function generates a single winning number for all tickets in the commit
     *         and processes all tickets at once for gas efficiency
     */
    function revealTicket(uint256 commitId) external nonReentrant {
        CommitInfo storage commit = commits[commitId];
        require(commit.user != address(0), "Commit not found");
        require(!commit.revealed, "Already revealed");
        
        uint256 targetBlock = commit.commitBlock + commit.offsetUsed;
        require(block.number > targetBlock, "Too early to reveal");
        require(block.number <= targetBlock + 256, "Reveal window expired");
        
        bytes32 blockHash = blockhash(targetBlock);
        require(blockHash != bytes32(0), "Block hash unavailable");
        
        // Generate unified winning number for this batch
        bytes32 randomHash = keccak256(abi.encode(
            commit.user,
            commitId,
            blockHash,
            commit.numberRange,
            commit.salt
        ));
        
        uint8 winningNumber = uint8((uint256(randomHash) % commit.numberRange) + 1);
        commit.winningNumber = winningNumber;
        commit.revealed = true;
        
        // Process all tickets directly from commitTickets array
        TicketInfo[] storage tickets = commitTickets[commitId];
        uint256 totalWinnings = 0;
        
        for (uint256 i = 0; i < tickets.length; i++) {
            if (tickets[i].chosenNumber == winningNumber) {
                uint256 rewardAmount = _processWinningTicket(commitId, i, commit, tickets[i]);
                if (rewardAmount > 0) {
                    totalWinnings += rewardAmount;
                    commit.won = true;
                }
            }
        }
        
        // Update commit statistics
        commit.totalWinnings = totalWinnings;
        
        emit TicketRevealed(
            commitId,
            commit.user,
            commit.numberRange,
            tickets.length,
            commit.totalAmount,
            winningNumber,
            commit.won,
            totalWinnings,
            commit.rtpPercent
        );
    }
    
    /**
     * @dev Processes a winning ticket - reduces stack depth in revealTicket
     * @param commitId The commit ID
     * @param ticketIndex The index of the winning ticket
     * @param commit The commit information
     * @param ticket The winning ticket
     * @return rewardAmount The actual reward amount paid
     */
    function _processWinningTicket(
        uint256 commitId,
        uint256 ticketIndex, 
        CommitInfo storage commit,
        TicketInfo storage ticket
    ) internal returns (uint256 rewardAmount) {
        uint256 base = ticket.ticketAmount * commit.rtpPercent / BASE_POINT;
        rewardAmount = base * commit.numberRange;
        uint256 availableBalance = prizeToken.balanceOf(address(this));
        
        if (availableBalance >= rewardAmount) {
            // Process fees on winning amount
            uint256 burnAmount = (rewardAmount * burnFeePercent) / BASE_POINT;
            uint256 protocolAmount = (rewardAmount * protocolFeePercent) / BASE_POINT;
            uint256 actualReward = rewardAmount - burnAmount - protocolAmount;
            
            // Update statistics
            totalRewardsPaid += rewardAmount;
            ticket.rewardPaid = rewardAmount;
            
            // Transfer actual reward to user
            require(prizeToken.transfer(commit.user, actualReward), "Reward transfer failed");
            
            // Process fees
            if (burnAmount > 0) {
                prizeToken.burn(burnAmount);
                totalBurnedAmount += burnAmount;
            }
            
            if (protocolAmount > 0) {
                require(prizeToken.transfer(protocolFeeRecipient, protocolAmount), "Protocol fee failed");
                totalProtocolFees += protocolAmount;
            }
            
            // Emit fees processed event
            emit FeesProcessed(commitId, rewardAmount, burnAmount, protocolAmount);
            
        } else {
            emit InsufficientPrizePool(commitId, commit.user, ticketIndex, rewardAmount, availableBalance);
            rewardAmount = 0;
        }
    }
    
    // ==================== Internal Functions ====================
    
    /**
     * @dev Verifies the EIP-712 commit signature with enhanced security
     * @param user Address of the user making the commit
     * @param numberRange Number range used
     * @param choices Array of number choices
     * @param salt Random salt value
     * @param nonce User nonce
     * @param deadline Signature expiry timestamp
     * @param signature EIP-712 signature to verify
     */
    function _verifyCommitSignature(
        address user,
        uint8 numberRange,
        NumberChoice[] calldata choices,
        uint256 salt,
        uint256 nonce,
        uint256 deadline,
        bytes calldata signature
    ) internal view {
        require(block.timestamp <= deadline, "Signature expired");
        require(usedNonce[user] < nonce, "Nonce already used");
        
        // Construct EIP-712 structured data hash
        bytes32 structHash = keccak256(abi.encode(
            COMMIT_TICKET_TYPEHASH,
            user,
            numberRange,
            _hashChoices(choices),
            salt,
            nonce,
            deadline
        ));
        
        bytes32 digest = keccak256(abi.encodePacked(
            "\x19\x01",
            DOMAIN_SEPARATOR,
            structHash
        ));
        
        address signer = _recoverSignerSecure(digest, signature);
        require(signer == trustedSigner, "Invalid signature");
    }
    
    /**
     * @dev Hashes an array of NumberChoice structs for EIP-712
     * @param choices Array of number choices to hash
     * @return Hash of the choices array
     */
    function _hashChoices(NumberChoice[] calldata choices) internal pure returns (bytes32) {
        bytes32[] memory choiceHashes = new bytes32[](choices.length);
        
        for (uint256 i = 0; i < choices.length; i++) {
            choiceHashes[i] = keccak256(abi.encode(
                NUMBER_CHOICE_TYPEHASH,
                choices[i].ticketAmount,
                choices[i].chosenNumber
            ));
        }
        
        return keccak256(abi.encodePacked(choiceHashes));
    }
    
    /**
     * @dev Securely recovers the signer address from a signature with enhanced validation
     * @param hash The hash that was signed
     * @param signature The signature to recover from
     * @return The address that created the signature
     */
    function _recoverSignerSecure(bytes32 hash, bytes calldata signature) internal pure returns (address) {
        require(signature.length == 65, "Invalid signature length");
        
        bytes32 r;
        bytes32 s;
        uint8 v;
        
        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 0x20))
            v := byte(0, calldataload(add(signature.offset, 0x40)))
        }
        
        // Validate v parameter (must be 27 or 28)
        require(v == 27 || v == 28, "Invalid v value");
        
        // Validate s parameter to prevent signature malleability
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid s value");
        
        // Recover signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "Invalid signature");
        
        return signer;
    }
    
    // ==================== Query Functions ====================
    
    /**
     * @dev Gets all tickets for a user across all their commits
     * @param user Address of the user
     * @return commitIds Array of commit IDs
     * @return tickets Array of ticket arrays for each commit
     */
    function getUserTickets(address user) 
        external view returns (uint256[] memory commitIds, TicketInfo[][] memory tickets) {
        uint256[] memory userCommitIds = userCommits[user];
        commitIds = userCommitIds;
        tickets = new TicketInfo[][](userCommitIds.length);
        
        for (uint256 i = 0; i < userCommitIds.length; i++) {
            uint256 commitId = userCommitIds[i];
            tickets[i] = commitTickets[commitId];
        }
    }
    
    /**
     * @dev Gets a paginated list of user's commits
     * @param user Address of the user
     * @param offset Starting index for pagination
     * @param limit Maximum number of commits to return
     * @return Array of commit IDs
     */
    function getUserCommits(address user, uint256 offset, uint256 limit)
        external view returns (uint256[] memory) {
        uint256 total = userCommits[user].length;
        if (offset >= total) return new uint256[](0);
        
        uint256 end = offset + limit;
        if (end > total) end = total;
        
        uint256[] memory result = new uint256[](end - offset);
        for (uint256 i = offset; i < end; i++) {
            result[i - offset] = userCommits[user][i];
        }
        return result;
    }
    
    /**
     * @dev Gets all tickets for a specific commit
     * @param commitId ID of the commit
     * @return Array of ticket information in the commit
     */
    function getCommitTickets(uint256 commitId) external view returns (TicketInfo[] memory) {
        return commitTickets[commitId];
    }
    
    /**
     * @dev Gets a specific ticket within a commit
     * @param commitId ID of the commit
     * @param ticketIndex Index of the ticket within the commit
     * @return Ticket information
     */
    function getTicket(uint256 commitId, uint256 ticketIndex) external view returns (TicketInfo memory) {
        require(ticketIndex < commitTickets[commitId].length, "Ticket index out of bounds");
        return commitTickets[commitId][ticketIndex];
    }
    
    /**
     * @dev Gets comprehensive lottery statistics
     * @return totalPrizePool Current balance of the prize pool
     * @return blockOffset Number of blocks to wait before reveal
     * @return totalTickets Total number of tickets created
     * @return totalCommits Total number of commits made
     * @return totalRewards Total rewards paid out
     * @return minRange Minimum allowed number range
     * @return maxRange Maximum allowed number range
     * @return maxTicketAmountReturn Maximum bet amount per ticket
     * @return minTicketAmountReturn Minimum bet amount per ticket
     * @return rtpPercentReturn Current RTP percentage in basis points
     */
    function getLotteryStats() external view returns (
        uint256 totalPrizePool,
        uint256 blockOffset,
        uint256 totalTickets,
        uint256 totalCommits,
        uint256 totalRewards,
        uint8 minRange,
        uint8 maxRange,
        uint256 maxTicketAmountReturn,
        uint256 minTicketAmountReturn,
        uint256 rtpPercentReturn
    ) {
        return (
            prizeToken.balanceOf(address(this)),
            futureBlockOffset,
            totalTicketCount,
            totalCommitCount,
            totalRewardsPaid,
            minNumberRange,
            maxNumberRange,
            maxTicketAmount,
            minTicketAmount,
            rtpPercent
        );
    }
    
    /**
     * @dev Gets detailed information about a specific commit
     * @param commitId ID of the commit to query
     * @return commit Commit information
     * @return tickets Array of tickets in this commit
     */
    function getCommitDetails(uint256 commitId) external view returns (
        CommitInfo memory commit,
        TicketInfo[] memory tickets
    ) {
        commit = commits[commitId];
        tickets = commitTickets[commitId];
    }
    
    /**
     * @dev Calculates potential total rewards with validation
     * @param numberRange The number range to use
     * @param choices Array of number choices
     * @return totalCost Total cost of all tickets
     * @return maxSingleReward Maximum possible reward for a single winning number
     * 
     * @notice This function validates the choices and calculates the total cost and maximum possible reward
     *         for a single winning number (since only one number can win per draw)
     */
    function calculateTotalReward(uint8 numberRange, NumberChoice[] calldata choices) 
        external view returns (uint256 totalCost, uint256 maxSingleReward) {
        
        // Use existing validation function to avoid duplicate code
        validateChoices(numberRange, choices);
        
        // Calculate total cost and find the maximum reward for any single winning number
        for (uint256 i = 0; i < choices.length; i++) {
            totalCost += choices[i].ticketAmount;
            
            // Calculate reward for this specific number if it wins
            uint256 currentReward = (choices[i].ticketAmount * numberRange * rtpPercent) / BASE_POINT;
            
            // Keep track of the highest possible single number reward
            if (currentReward > maxSingleReward) {
                maxSingleReward = currentReward;
            }
        }
    }
    
    /**
     * @dev Gets win probability and maximum choices for a specific number range
     * @param numberRange The number range to query
     * @return winProbabilityBP Win probability in basis points
     * @return maxPossibleChoices Maximum number of choices possible
     * @return rtpMultiplier RTP-based reward multiplier for winning tickets
     */
    function getRangeInfo(uint8 numberRange) 
        external view returns (
            uint256 winProbabilityBP,
            uint256 maxPossibleChoices,
            uint256 rtpMultiplier
        ) {
        require(numberRange >= minNumberRange && numberRange <= maxNumberRange, "Invalid range");
        
        return (
            BASE_POINT / numberRange,  // Win probability (basis points): 10000/numberRange
            numberRange,               // Maximum possible choices equals numberRange
            rtpPercent                 // RTP-based reward multiplier in basis points
        );
    }
    
    // ==================== Administrative Functions ====================
    
    /**
     * @dev Funds the jackpot/prize pool
     * @param amount Amount of tokens to add to the prize pool
     * 
     * @notice Anyone can fund the jackpot to increase the prize pool
     */
    function fundJackpot(uint256 amount) external nonReentrant {
        require(prizeToken.transferFrom(msg.sender, address(this), amount), "Transfer failed");
        emit JackpotFunded(msg.sender, amount, prizeToken.balanceOf(address(this)));
    }
    
    /**
     * @dev Emergency withdrawal function (Owner only)
     * @param to Address to send tokens to
     * @param amount Amount of tokens to withdraw
     * 
     * @notice This is an emergency function that should only be used in exceptional circumstances
     */
    function emergencyWithdraw(address to, uint256 amount) external onlyOwner nonReentrant {
        require(to != address(0), "Invalid address");
        require(prizeToken.transfer(to, amount), "Transfer failed");
    }
    
    /**
     * @dev Sets the fee configuration
     * @param _burnPercent Percentage to burn (in basis points)
     * @param _protocolPercent Percentage for protocol fee (in basis points)
     */
    function setFeeConfig(uint256 _burnPercent, uint256 _protocolPercent) external onlyOwner {
        require(_burnPercent <= 1000 && _protocolPercent <= 1000, "Fee too high");
        burnFeePercent = _burnPercent;
        protocolFeePercent = _protocolPercent;
    }
    
    /**
     * @dev Sets the trusted signer address
     * @param newSigner New trusted signer address
     */
    function setTrustedSigner(address newSigner) external onlyOwner {
        require(newSigner != address(0), "Invalid signer");
        trustedSigner = newSigner;
    }
    
    /**
     * @dev Sets the default deadline duration for signatures
     * @param _defaultDeadlineDuration New default deadline duration in seconds
     */
    function setDefaultDeadlineDuration(uint256 _defaultDeadlineDuration) external onlyOwner {
        require(_defaultDeadlineDuration > 0, "Invalid deadline duration");
        require(_defaultDeadlineDuration <= 3600, "Deadline too long"); // Max 1 hour
        
        uint256 oldDuration = defaultDeadlineDuration;
        defaultDeadlineDuration = _defaultDeadlineDuration;
        
        emit DefaultDeadlineDurationUpdated(oldDuration, _defaultDeadlineDuration);
    }
    
    /**
     * @dev Pauses the contract (Owner only)
     */
    function pause() external onlyOwner {
        _pause();
    }
    
    /**
     * @dev Unpauses the contract (Owner only)
     */
    function unpause() external onlyOwner {
        _unpause();
    }
    
    /**
     * @dev Sets the allowed number range limits (Owner only)
     * @param _minRange New minimum number range (must be >= 2)
     * @param _maxRange New maximum number range (must be <= 255)
     * 
     * @notice This function allows the owner to adjust the supported number ranges
     *         for lottery games. Changes take effect immediately for new commits.
     *         Existing commits are not affected.
     */
    function setNumberRanges(uint8 _minRange, uint8 _maxRange) external onlyOwner {
        require(_minRange >= 2, "Min range must be at least 2");
        require(_maxRange <= 255, "Max range too large");
        require(_minRange < _maxRange, "Min must be less than max");
        require(_maxRange - _minRange >= 1, "Range span too small");
        
        uint8 oldMinRange = minNumberRange;
        uint8 oldMaxRange = maxNumberRange;
        
        minNumberRange = _minRange;
        maxNumberRange = _maxRange;
        
        emit NumberRangesUpdated(oldMinRange, oldMaxRange, _minRange, _maxRange);
    }
    
    /**
     * @dev Gets the current number range configuration
     * @return minRange Current minimum number range
     * @return maxRange Current maximum number range
     * 
     * @notice Initial values are: minRange=2, maxRange=32
     */
    function getNumberRangeConfig() external view returns (
        uint8 minRange,
        uint8 maxRange
    ) {
        return (
            minNumberRange,
            maxNumberRange
        );
    }
    
    /**
     * @dev Sets the RTP (Return to Player) percentage (Owner only)
     * @param _rtpPercent New RTP percentage in basis points (max 10000 = 100%)
     * 
     * @notice This function allows the owner to adjust the reward percentage.
     *         Higher RTP means higher rewards for winners but requires more prize pool funds.
     */
    function setRtpPercent(uint256 _rtpPercent) external onlyOwner {
        require(_rtpPercent > 0 && _rtpPercent <= BASE_POINT, "Invalid RTP percent");
        
        uint256 oldRtpPercent = rtpPercent;
        rtpPercent = _rtpPercent;
        
        emit RtpPercentUpdated(oldRtpPercent, _rtpPercent);
    }
    
    /**
     * @dev Sets the future block offset for reveal timing (Owner only)
     * @param _futureBlockOffset New number of blocks to wait before reveal
     * 
     * @notice This function allows the owner to adjust the timing requirement for reveals.
     *         Changes take effect immediately for new commits.
     *         Existing commits are not affected.
     */
    function setFutureBlockOffset(uint256 _futureBlockOffset) external onlyOwner {
        require(_futureBlockOffset > 0 && _futureBlockOffset < 256, "Block offset must be positive and less than 256");
        
        uint256 oldOffset = futureBlockOffset;
        futureBlockOffset = _futureBlockOffset;
        
        emit FutureBlockOffsetUpdated(oldOffset, _futureBlockOffset);
    }
    
    /**
     * @dev Sets the minimum and maximum ticket amounts (Owner only)
     * @param _minTicketAmount New minimum bet amount per ticket
     * @param _maxTicketAmount New maximum bet amount per ticket
     * 
     * @notice This function allows the owner to adjust bet amount limits.
     *         Changes take effect immediately for new commits.
     *         Existing commits are not affected.
     */
    function setTicketAmountLimits(uint256 _minTicketAmount, uint256 _maxTicketAmount) external onlyOwner {
        require(_minTicketAmount > 0, "Min amount must be positive");
        require(_maxTicketAmount > 0, "Max amount must be positive");
        require(_minTicketAmount <= _maxTicketAmount, "Min amount must be <= max amount");
        
        uint256 oldMinAmount = minTicketAmount;
        uint256 oldMaxAmount = maxTicketAmount;
        
        minTicketAmount = _minTicketAmount;
        maxTicketAmount = _maxTicketAmount;
        
        emit TicketAmountLimitsUpdated(oldMinAmount, oldMaxAmount, _minTicketAmount, _maxTicketAmount);
    }
    
    // ==================== Utility Functions ====================
    
    /**
     * @dev Gets the current EIP-712 domain separator
     * @return The domain separator hash
     */
    function getDomainSeparator() external view returns (bytes32) {
        return DOMAIN_SEPARATOR;
    }
    
    /**
     * @dev Gets the recommended deadline for new signatures
     * @return Recommended deadline timestamp (current time + default duration)
     */
    function getRecommendedDeadline() external view returns (uint256) {
        return block.timestamp + defaultDeadlineDuration;
    }
}
        

@openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
          

@openzeppelin/contracts/security/Pausable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}
          

@openzeppelin/contracts/security/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
          

@openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
          

@openzeppelin/contracts/utils/cryptography/ECDSA.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}
          

Compiler Settings

{"viaIR":true,"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":1000000,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_signer","internalType":"address"},{"type":"address","name":"_prizeToken","internalType":"address"},{"type":"uint256","name":"_maxTicketAmount","internalType":"uint256"},{"type":"uint256","name":"_minTicketAmount","internalType":"uint256"},{"type":"uint256","name":"_offset","internalType":"uint256"},{"type":"address","name":"_protocolFeeRecipient","internalType":"address"},{"type":"uint256","name":"_rtpPercent","internalType":"uint256"}]},{"type":"event","name":"DefaultDeadlineDurationUpdated","inputs":[{"type":"uint256","name":"oldDuration","internalType":"uint256","indexed":false},{"type":"uint256","name":"newDuration","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeesProcessed","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256","indexed":true},{"type":"uint256","name":"totalAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"burnAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"protocolAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FutureBlockOffsetUpdated","inputs":[{"type":"uint256","name":"oldOffset","internalType":"uint256","indexed":false},{"type":"uint256","name":"newOffset","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"InsufficientPrizePool","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256","indexed":true},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"ticketIndex","internalType":"uint256","indexed":true},{"type":"uint256","name":"requiredAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"availableAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"JackpotFunded","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newBalance","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NumberRangesUpdated","inputs":[{"type":"uint8","name":"oldMinRange","internalType":"uint8","indexed":false},{"type":"uint8","name":"oldMaxRange","internalType":"uint8","indexed":false},{"type":"uint8","name":"newMinRange","internalType":"uint8","indexed":false},{"type":"uint8","name":"newMaxRange","internalType":"uint8","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RtpPercentUpdated","inputs":[{"type":"uint256","name":"oldRtpPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"newRtpPercent","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TicketAmountLimitsUpdated","inputs":[{"type":"uint256","name":"oldMinAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"oldMaxAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newMinAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newMaxAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TicketCommitted","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256","indexed":true},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint8","name":"numberRange","internalType":"uint8","indexed":false},{"type":"uint256","name":"choiceCount","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"targetBlock","internalType":"uint256","indexed":false},{"type":"uint256","name":"rtpPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"offsetUsed","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TicketRevealed","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256","indexed":true},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint8","name":"numberRange","internalType":"uint8","indexed":false},{"type":"uint256","name":"choiceCount","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalAmount","internalType":"uint256","indexed":false},{"type":"uint8","name":"winningNumber","internalType":"uint8","indexed":false},{"type":"bool","name":"won","internalType":"bool","indexed":true},{"type":"uint256","name":"totalWinnings","internalType":"uint256","indexed":false},{"type":"uint256","name":"rtpPercent","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BASE_POINT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"COMMIT_TICKET_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"NUMBER_CHOICE_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"burnFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"totalCost","internalType":"uint256"},{"type":"uint256","name":"maxSingleReward","internalType":"uint256"}],"name":"calculateTotalReward","inputs":[{"type":"uint8","name":"numberRange","internalType":"uint8"},{"type":"tuple[]","name":"choices","internalType":"struct GLucky1V2.NumberChoice[]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"commitTicket","inputs":[{"type":"uint8","name":"numberRange","internalType":"uint8"},{"type":"tuple[]","name":"choices","internalType":"struct GLucky1V2.NumberChoice[]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]},{"type":"uint256","name":"salt","internalType":"uint256"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint256","name":"rewardPaid","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}],"name":"commitTickets","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"totalAmount","internalType":"uint256"},{"type":"uint256","name":"commitBlock","internalType":"uint256"},{"type":"uint256","name":"salt","internalType":"uint256"},{"type":"uint256","name":"totalWinnings","internalType":"uint256"},{"type":"uint256","name":"rtpPercent","internalType":"uint256"},{"type":"uint256","name":"offsetUsed","internalType":"uint256"},{"type":"uint8","name":"numberRange","internalType":"uint8"},{"type":"uint8","name":"winningNumber","internalType":"uint8"},{"type":"bool","name":"revealed","internalType":"bool"},{"type":"bool","name":"won","internalType":"bool"}],"name":"commits","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"defaultDeadlineDuration","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"emergencyWithdraw","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fundJackpot","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"futureBlockOffset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"commit","internalType":"struct GLucky1V2.CommitInfo","components":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"totalAmount","internalType":"uint256"},{"type":"uint256","name":"commitBlock","internalType":"uint256"},{"type":"uint256","name":"salt","internalType":"uint256"},{"type":"uint256","name":"totalWinnings","internalType":"uint256"},{"type":"uint256","name":"rtpPercent","internalType":"uint256"},{"type":"uint256","name":"offsetUsed","internalType":"uint256"},{"type":"uint8","name":"numberRange","internalType":"uint8"},{"type":"uint8","name":"winningNumber","internalType":"uint8"},{"type":"bool","name":"revealed","internalType":"bool"},{"type":"bool","name":"won","internalType":"bool"}]},{"type":"tuple[]","name":"tickets","internalType":"struct GLucky1V2.TicketInfo[]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint256","name":"rewardPaid","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}],"name":"getCommitDetails","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct GLucky1V2.TicketInfo[]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint256","name":"rewardPaid","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}],"name":"getCommitTickets","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getDomainSeparator","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"totalPrizePool","internalType":"uint256"},{"type":"uint256","name":"blockOffset","internalType":"uint256"},{"type":"uint256","name":"totalTickets","internalType":"uint256"},{"type":"uint256","name":"totalCommits","internalType":"uint256"},{"type":"uint256","name":"totalRewards","internalType":"uint256"},{"type":"uint8","name":"minRange","internalType":"uint8"},{"type":"uint8","name":"maxRange","internalType":"uint8"},{"type":"uint256","name":"maxTicketAmountReturn","internalType":"uint256"},{"type":"uint256","name":"minTicketAmountReturn","internalType":"uint256"},{"type":"uint256","name":"rtpPercentReturn","internalType":"uint256"}],"name":"getLotteryStats","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"minRange","internalType":"uint8"},{"type":"uint8","name":"maxRange","internalType":"uint8"}],"name":"getNumberRangeConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"winProbabilityBP","internalType":"uint256"},{"type":"uint256","name":"maxPossibleChoices","internalType":"uint256"},{"type":"uint256","name":"rtpMultiplier","internalType":"uint256"}],"name":"getRangeInfo","inputs":[{"type":"uint8","name":"numberRange","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRecommendedDeadline","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct GLucky1V2.TicketInfo","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint256","name":"rewardPaid","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}],"name":"getTicket","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256"},{"type":"uint256","name":"ticketIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getUserCommits","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"offset","internalType":"uint256"},{"type":"uint256","name":"limit","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"commitIds","internalType":"uint256[]"},{"type":"tuple[][]","name":"tickets","internalType":"struct GLucky1V2.TicketInfo[][]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint256","name":"rewardPaid","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}],"name":"getUserTickets","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"maxNumberRange","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxTicketAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"minNumberRange","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minTicketAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20Burnable"}],"name":"prizeToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"protocolFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"protocolFeeRecipient","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revealTicket","inputs":[{"type":"uint256","name":"commitId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rtpPercent","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDefaultDeadlineDuration","inputs":[{"type":"uint256","name":"_defaultDeadlineDuration","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeConfig","inputs":[{"type":"uint256","name":"_burnPercent","internalType":"uint256"},{"type":"uint256","name":"_protocolPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFutureBlockOffset","inputs":[{"type":"uint256","name":"_futureBlockOffset","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setNumberRanges","inputs":[{"type":"uint8","name":"_minRange","internalType":"uint8"},{"type":"uint8","name":"_maxRange","internalType":"uint8"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRtpPercent","inputs":[{"type":"uint256","name":"_rtpPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTicketAmountLimits","inputs":[{"type":"uint256","name":"_minTicketAmount","internalType":"uint256"},{"type":"uint256","name":"_maxTicketAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTrustedSigner","inputs":[{"type":"address","name":"newSigner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalBurnedAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalCommitCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalProtocolFees","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalRewardsPaid","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalTicketCount","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"trustedSigner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"usedNonce","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"userCommits","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[],"name":"validateChoices","inputs":[{"type":"uint8","name":"numberRange","internalType":"uint8"},{"type":"tuple[]","name":"choices","internalType":"struct GLucky1V2.NumberChoice[]","components":[{"type":"uint256","name":"ticketAmount","internalType":"uint256"},{"type":"uint8","name":"chosenNumber","internalType":"uint8"}]}]}]
              

Contract Creation Code

Verify & Publish
0x60c03462000481576001600160401b0390601f6200496f38819003918201601f191683019291908484118385101762000486578160e09284926040968752833981010312620004815762000053816200049c565b91602091620000648382016200049c565b82820151606083015160808401519160c06200008360a087016200049c565b95015194600098895488519660018060a01b0391338d7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08584169180a36001600160a81b0319163360ff60a01b1916178c556001805560786005556006546101f460078190556008559282169788156200044e575081169283156200041a578415620003e1578515620003a8578486116200036657169586156200032e578515158062000322575b15620002de5787151580620002d1575b156200028d5761318160a11b9160018060b01b031916171760065560805260025560035560045560018060a01b03196009541617600955600a558051918201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f84527f7f103ea7b80af2cfe9b76c007f9656a88381f7c35a632465293ef26a7d5459a0828401527f88f72b566ae0c96f6fffac4bc8ac74909f61512ac0c06a8124d5ed420d306f9060608401524660808401523060a084015260a0835260c08301948386109086111762000279575083905251902060a0526144bd9081620004b28239608051818181610c3801528181610fc4015281816110c3015281816112090152818161132c0152818161139c01528181611b8501528181611d2c0152818161206b0152612543015260a0518181816123b80152613b320152f35b634e487b7160e01b81526041600452602490fd5b885162461bcd60e51b8152600481018b9052601360248201527f496e76616c6964205254502070657263656e74000000000000000000000000006044820152606490fd5b506127108811156200013b565b885162461bcd60e51b8152600481018b9052601460248201527f496e76616c696420626c6f636b206f66667365740000000000000000000000006044820152606490fd5b5061010086106200012b565b885162461bcd60e51b8152600481018b90526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b6044820152606490fd5b60648b8b519062461bcd60e51b825280600483015260248201527f4d696e20616d6f756e74206d757374206265203c3d206d617820616d6f756e746044820152fd5b895162461bcd60e51b8152600481018c90526012602482015271125b9d985b1a59081b5a5b88185b5bdd5b9d60721b6044820152606490fd5b895162461bcd60e51b8152600481018c90526012602482015271125b9d985b1a59081b585e08185b5bdd5b9d60721b6044820152606490fd5b895162461bcd60e51b8152600481018c9052600d60248201526c24b73b30b634b2103a37b5b2b760991b6044820152606490fd5b62461bcd60e51b8152600481018c9052600e60248201526d24b73b30b634b21039b4b3b732b960911b6044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620004815756fe608080604052600436101561001357600080fd5b60003560e01c9081630484a22f146137a9575080631135daeb1461376e57806314558fab146137325780631c91bed7146136b1578063266f16a11461360f57806335ab5f64146135d35780633644e515146106f15780633e61e9dc1461356e5780633f4ba83a14613460578063431c73bd146132b65780634e0856a71461327a578063532f845d1461321c57806356a1c7011461312d578063577632c014613114578063595fc101146130bf5780635b3a8a2e14612dd15780635c975abb14612d8d5780636456110314612d5157806364df049e14612cff578063699fa50f14612cc3578063715018a614612c23578063737500e11461216d57806374958e35146121315780637517f5ff14611ff3578063774aa16814611fb75780637a58073614611f005780638456cb5914611e355780638571c44114611c935780638da5cb5b14611c4157806395ccea6714611ab35780639e905ac114611a5a578063a284b6d314611a1e578063a3999356146118f3578063a426e4c81461180f578063a6d64abf146117c3578063b2457e7614610db2578063c6b91f4b14610d70578063c7c4a61514610c98578063c7d77b1114610c5c578063d0ef024a14610bed578063d47afe1314610b94578063d6e6eb9f14610b58578063d8f4fa5e146109ff578063dc46c2411461095a578063e829482814610854578063ec5aa5381461080f578063ecb3c3af146106f6578063ed24911d146106f1578063f02138bb146106af578063f2fde38b14610575578063f526e39a14610539578063f74d5480146104e7578063fb64bd6a146104ab5763ffdb16521461027157600080fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760006101406040516102b18161409e565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201528261012082015201526004356000526010602052604060002060ff60076040519261030f8461409e565b73ffffffffffffffffffffffffffffffffffffffff815416845260018101546020850152600281015460408501526003810154606085015260048101546080850152600581015460a0850152600681015460c0850152015481811660e0840152818160081c16610100840152818160101c16151561012084015260181c161515610140820152600435600052601160205260406000209081546103b1816142ac565b926103bf60405194856140d7565b818452602084019060005260206000206000915b838310610488576101408561048488604051938373ffffffffffffffffffffffffffffffffffffffff8695511685526020810151602086015260408101516040860152606081015160608601526080810151608086015260a081015160a086015260c081015160c086015260ff60e08201511660e086015260ff6101008201511661010086015261012081015115156101208601520151151561014084015261018080610160850152830190613aa4565b0390f35b60036020600192610498856142d8565b8152019201920191906103d3565b600080fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600354604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600f54604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576105ac613a06565b6000549073ffffffffffffffffffffffffffffffffffffffff808316916105d4338414613c42565b1691821561062b577fffffffffffffffffffffffff0000000000000000000000000000000000000000839116176000557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60065460a81c16604051908152f35b613afc565b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65761072d613b89565b60ff60065491169060ff8160a01c1682101590816107fe575b50156107a057801561077157600a546040805161271084900481526020810193909352820152606090f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f496e76616c69642072616e6765000000000000000000000000000000000000006044820152fd5b60ff915060a81c1681111582610746565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602061084c60055442614202565b604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576004356108a973ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8015158061094e575b156108f05760407f10e19a1a714c086ca5710ed885ba6097611170f0d62b2fe5f97e586f6ea7318591600a549080600a5582519182526020820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e76616c6964205254502070657263656e74000000000000000000000000006044820152fd5b506127108111156108b2565b346104a65761096836613bca565b909160009182919061097b818684613ca7565b600091600a54915b80841061099a576040868682519182526020820152f35b909192946109b4906109ad87848a614072565b3590614202565b946127106109da856109d56109ca85878d614072565b3560ff881690614227565b614227565b048581116109f5575b506109ed90614045565b929190610983565b94506109ed6109e3565b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657600435610a5473ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8015610afa57610e108111610a9c5760407f576363f65c117fad2e20056ea0e05d0b42d33a4bd5507b89591b0ab732b6878091600554908060055582519182526020820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f446561646c696e6520746f6f206c6f6e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420646561646c696e65206475726174696f6e000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600854604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517f696dd1dfe35e9837d052c32a94268b1e30279d02e5abdc07018dc2a61f4fe1128152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600a54604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576004356000526010602052610160604060002060ff73ffffffffffffffffffffffffffffffffffffffff82541691600181015490600281015460038201546004830154906005840154926007600686015495015495604051988952602089015260408801526060870152608086015260a085015260c084015281811660e0840152818160081c16610100840152818160101c16151561012084015260181c161515610140820152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60065460a01c16604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657610df06002600154141561419d565b60026001556004356000526010602052604060002073ffffffffffffffffffffffffffffffffffffffff81541680156117655760078201549060ff8260101c1661170757610e476002840154600685015490614202565b804311156116a95761010081018082116115be57431161164b57409182156115ed5760ff1691600384015460405191602083019384526004356040840152606083015283608083015260a082015260a0815260c0810181811067ffffffffffffffff8211176113f65760405251902081156107715706600181018091116115be576007820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff1661ff00600884901b1617620100001790556004356000908152601160205260408120909290835b815481101561152d57610f2a8183613c26565b5060ff60028187169201541614610f4a575b610f4590614045565b610f17565b610f548183613c26565b50610f7a612710610f6b8354600588015490614227565b0460ff60078701541690614227565b906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156113095784916000916114f8575b508381106114a257505061271061100f60075484614227565b04906110a8602061271061102560085487614227565b049285600161103d86611038898561423a565b61423a565b92600d61104b848254614202565b9055015588546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481019190915291829081906044820190565b0381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af190811561130957600091611483575b50156114255781611315575b80611199575b604080518481526020810193909352820152600435907f8891369dd1f0ce0c069cb737b0a2ed608f94382ccad96d5fe2d7630170fc655d9080606081015b0390a25b80611154575b50610f3c565b61116290610f459296614202565b6007840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff166301000000179055949061114e565b6009546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024810182905260208180604481010381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1908115611309576000916112da575b501561127c576111447f8891369dd1f0ce0c069cb737b0a2ed608f94382ccad96d5fe2d7630170fc655d91600f611272828254614202565b9055915050611106565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f50726f746f636f6c20666565206661696c6564000000000000000000000000006044820152fd5b6112fc915060203d602011611302575b6112f481836140d7565b81019061420f565b8961123a565b503d6112ea565b6040513d6000823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156104a6576040517f42966c680000000000000000000000000000000000000000000000000000000081528260048201526000816024818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015611309576113de575b50600e6113d7838254614202565b9055611100565b67ffffffffffffffff81116113f657604052886113c9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f526577617264207472616e73666572206661696c6564000000000000000000006044820152fd5b61149c915060203d602011611302576112f481836140d7565b896110f4565b91509173ffffffffffffffffffffffffffffffffffffffff8654169160405191825260208201527f1f9cd3d952b325a18cdca37b5aea2f219d8375edb61c9fa2a6e467e45fcebd0a604060043592a46000611148565b9150506020813d602011611525575b81611514602093836140d7565b810103126104a65783905189610ff6565b3d9150611507565b5060ff90849283600482015573ffffffffffffffffffffffffffffffffffffffff815416938360078301549354966005600185015494015493604051988387168a5260208a01526040890152166060870152608086015260a085015260181c161515917f3cdef9cb42e9d23d0fde40055cad23a024c0f73e8e7a5f125682946fda31dbd960c060043592a460018055005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f426c6f636b206861736820756e617661696c61626c65000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f52657665616c2077696e646f77206578706972656400000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f546f6f206561726c7920746f2072657665616c000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f416c72656164792072657665616c6564000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f436f6d6d6974206e6f7420666f756e64000000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657604060065460ff825191818160a01c16835260a81c166020820152f35b346104a65761181d36613b55565b60006040805161182c81614082565b82815282602082015201528160005260116020526040600020548110156118955761186961186f9160609360005260116020526040600020613c26565b506142d8565b611893604051809260ff604080928051855260208101516020860152015116910152565bf35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5469636b657420696e646578206f7574206f6620626f756e64730000000000006044820152fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043561194873ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b80151580611a13575b1561198f5760407f516783205ee62f5388530b90857153150ad3f46cd64a2029d165ecdc5254c71a91600454908060045582519182526020820152a1005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f426c6f636b206f6666736574206d75737420626520706f73697469766520616e60448201527f64206c657373207468616e2032353600000000000000000000000000000000006064820152fd5b506101008110611951565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600254604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517fef643a4aacdbb61aec2490bfc65cdfbf19319dc39c69cf404da063f49286362e8152f35b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657611aea613a06565b73ffffffffffffffffffffffffffffffffffffffff611b0e81600054163314613c42565b611b1d6002600154141561419d565b600260015580821615611be3576040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024803590830152602090829060449082906000907f0000000000000000000000000000000000000000000000000000000000000000165af1801561130957611bbf91600091611bc5575b5061444b565b60018055005b611bdd915060203d8111611302576112f481836140d7565b82611bb9565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043590611cd66002600154141561419d565b60026001556040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681836064816000855af190811561130957611d728392602495600091611e18575061444b565b604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa91821561130957600092611de9575b506040519283528201527f09c1e511cc9e2b5b245d0f85ff8a151b74969dbc1fb73f98973df6e8df1950ee60403392a260018055005b9080925081813d8311611e11575b611e0181836140d7565b810103126104a657519083611db3565b503d611df7565b611e2f9150843d8611611302576112f481836140d7565b87611bb9565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600054611ebf3373ffffffffffffffffffffffffffffffffffffffff831614613c42565b611ecf60ff8260a01c1615614138565b16176000557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346104a657611f0e36613b55565b90611f3273ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b6103e8808211159081611fac575b5015611f4e57600755600855005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f46656520746f6f206869676800000000000000000000000000000000000000006044820152fd5b905082111583611f40565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600454604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015611309576000906120fe575b61014090600454600b54600c5460ff600d54600654906002549360035495600a5497604051998a5260208a0152604089015260608801526080870152818160a01c1660a087015260a81c1660c085015260e0840152610100830152610120820152f35b506020813d8211612129575b81612117602093836140d7565b810103126104a657610140905161209b565b3d915061210a565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600d54604051908152f35b346104a65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576121a4613b89565b60243567ffffffffffffffff81116104a6576121c4903690600401613b99565b9067ffffffffffffffff60a435116104a65736602360a4350112156104a65767ffffffffffffffff60a43560040135116104a65736602460a4356004013560a4350101116104a65761221e60ff60005460a01c1615614138565b61222d6002600154141561419d565b600260015561223d828285613ca7565b6084354211612bc5573360005260136020526064356040600020541015612b6757612267826142ac565b61227460405191826140d7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06122a1846142ac565b0136602083013760005b838110612ae9575060405160208101818193602081519391019260005b818110612ad05750506123029250037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826140d7565b519020604051907f696dd1dfe35e9837d052c32a94268b1e30279d02e5abdc07018dc2a61f4fe112602083015233604083015260ff85166060830152608082015260443560a082015260643560c082015260843560e082015260e081528061010081011067ffffffffffffffff610100830111176113f657610100810160405261010081516020830120916101208101927f190100000000000000000000000000000000000000000000000000000000000084527f00000000000000000000000000000000000000000000000000000000000000006101228301526101428201526042828201526123f48282016140bb565b01519020604160a4356004013503612a7257601b606460a435013560001a148015612a5f575b15612a01577f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0604460a4350135116129a35760006080602092604051908152606460a4350135831a84820152602460a43501356040820152604460a4350135606082015282805260015afa15611309576124d073ffffffffffffffffffffffffffffffffffffffff600051166124b1811515614247565b73ffffffffffffffffffffffffffffffffffffffff6006541614614247565b3360005260136020526064356040600020556124ed600c54614045565b9182600c5560009160005b82811061298557506040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810184905260208160648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af190811561130957600091612966575b50156129085760005b828110612811575050600a54600454604051916125ae8361409e565b33835284602084015243604084015260443560608401526000608084015260a083015260c082015260ff851660e08201526000610100820152600061012082015260006101408201528360005260106020526007604060002073ffffffffffffffffffffffffffffffffffffffff8351167fffffffffffffffffffffffff00000000000000000000000000000000000000008254161781556020830151600182015560408301516002820155606083015160038201556080830151600482015560a0830151600582015560c08301516006820155019060ff60e0820151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354161782556126f360ff6101008301511683907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055565b6101208101511515907fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff62ff000063ff0000006101408654940151151560181b169360101b1691161717905533600052601260205260406000208054680100000000000000008110156113f65761276f91600182018155613a5d565b81549060031b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86831b921b19161790556127ad81600b54614202565b600b55600454916127be8343614202565b90600a549260ff60405197168752602087015260408601526060850152608084015260a08301527f556b5f8979ea10226082d75e6b6750390a02c97eeed3a6588b31f2d8ed3051ff60c03393a360018055005b8460005260116020526040600020906128366020612830838787614072565b01614118565b9160ff612844838787614072565b35936040519461285386614082565b8552600060208601521660408401528054680100000000000000008110156113f65761288491600182018155613c26565b9290926128d95760ff604060026128d495845181556020850151600182015501920151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055614045565b612592565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5061796d656e74206661696c65640000000000000000000000000000000000006044820152fd5b61297f915060203d602011611302576112f481836140d7565b86612589565b9261299861299e916109ad868686614072565b93614045565b6124f8565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c696420732076616c756500000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c696420762076616c756500000000000000000000000000000000006044820152fd5b50601c606460a435013560001a1461241a565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c6964207369676e6174757265206c656e67746800000000000000006044820152fd5b84518352602094850194869450909201916001016122c8565b80612af8612b62928686614072565b35612b096020612830848989614072565b6040519060208201927fef643a4aacdbb61aec2490bfc65cdfbf19319dc39c69cf404da063f49286362e8452604083015260ff60609116818301528152612b4f816140bb565b519020612b5c82856142c4565b52614045565b6122ab565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4e6f6e636520616c7265616479207573656400000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f5369676e617475726520657870697265640000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657600080547fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff821691612c9a338414613c42565b1682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600554604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60095416604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600c54604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60005460a01c166040519015158152f35b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657612e08613b89565b60249081359160ff808416928385036104a657612e3e73ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8181169260028410613061578285116130035784841015612fa557838503838111612f7757836001911610612f19577fe9f01c17475d67901c9556d8c8538ad113af4128eadd382ab327a2e91f0a20cf6080878787878774ff000000000000000000000000000000000000000075ff0000000000000000000000000000000000000000006006549660a81b169160a01b167fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff8616171760065560405193818160a01c16855260a81c16602084015260408301526060820152a1005b6064906014604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f52616e6765207370616e20746f6f20736d616c6c0000000000000000000000006044820152fd5b507f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6064906019604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d696e206d757374206265206c657373207468616e206d6178000000000000006044820152fd5b6064906013604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d61782072616e676520746f6f206c61726765000000000000000000000000006044820152fd5b606490601c604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d696e2072616e6765206d757374206265206174206c656173742032000000006044820152fd5b346104a6576130cd36613b55565b906000526011602052604060002080548210156104a6576060916130f091613c26565b5080549060ff60026001830154920154169060405192835260208301526040820152f35b346104a65761312b61312536613bca565b91613ca7565b005b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657613164613a06565b73ffffffffffffffffffffffffffffffffffffffff9061318982600054163314613c42565b1680156131be577fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655600080f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964207369676e65720000000000000000000000000000000000006044820152fd5b346104a65760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657610484613266613259613a06565b6044359060243590614303565b604051918291602083526020830190613a29565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600754604051908152f35b346104a6576132c436613b55565b6132e773ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b81156134025780156133a457808211613346577f6a49e336c3dd05f6936f44151964f6f99be0dd0f44e075566e0354b2928e225c91608091600354916002548160035582600255604051938452602084015260408301526060820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4d696e20616d6f756e74206d757374206265203c3d206d617820616d6f756e746044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d617820616d6f756e74206d75737420626520706f73697469766500000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d696e20616d6f756e74206d75737420626520706f73697469766500000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576000546134b33373ffffffffffffffffffffffffffffffffffffffff831614613c42565b60ff8160a01c1615613510577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166000557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65773ffffffffffffffffffffffffffffffffffffffff6135ba613a06565b1660005260136020526020604060002054604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600e54604051908152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043560005260118152604060002090815461365a816142ac565b9261366860405194856140d7565b8184526000908152828120838086015b84841061369257604051828152806104848185018a613aa4565b6001916003916136a1856142d8565b8152019201920191908490613678565b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576136e8613a06565b73ffffffffffffffffffffffffffffffffffffffff60243591166000526012602052604060002080548210156104a65760209161372491613a5d565b90546040519160031b1c8152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600b54604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040516127108152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65773ffffffffffffffffffffffffffffffffffffffff6137f6613a06565b1660005260128152816040600020805480835283830191600052836000209060005b8181106139ef578461387687613830838803846140d7565b82519261383c846142ac565b9361384a60405195866140d7565b8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09384916142ac565b018260005b8281106139df5750505060005b81518110156139225761389b81836142c4565b5160005260118352604060002080546138b3816142ac565b916138c160405193846140d7565b818352600090815285812090868085015b848310613903575050505050906138fe916138ed82886142c4565b526138f881876142c4565b50614045565b613888565b600191600391613912866142d8565b81520193019101909187906138d2565b5091929061393b60405193604085526040850190613a29565b908382038185015282519182815281810182808560051b8401019501966000925b8584106139695787870388f35b90919293868383839899030185528951908280835192838152019201906000905b8082106139a757505050988101989695946001019301919061395c565b91936060846139d160019496885160ff604080928051855260208101516020860152015116910152565b01940192018893929161398a565b606082828901015201839061387b565b825484528694509285019260019283019201613818565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104a657565b90815180825260208080930193019160005b828110613a49575050505090565b835185529381019392810192600101613a3b565b8054821015613a755760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90815180825260208080930193019160005b828110613ac4575050505090565b9091929382606082613af0600194895160ff604080928051855260208101516020860152015116910152565b01950193929101613ab6565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126104a6576004359060243590565b6004359060ff821682036104a657565b9181601f840112156104a65782359167ffffffffffffffff83116104a6576020808501948460061b0101116104a657565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104a65760043560ff811681036104a657916024359067ffffffffffffffff82116104a657613c2291600401613b99565b9091565b8054821015613a75576000526003602060002091020190600090565b15613c4957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b6006549060ff8091169181808260a01c168410159182614035575b505015613fd7578315613f7957818411613f1b5760035460025460005b868110613e2d5750505060409384519161200080840184811067ffffffffffffffff8211176113f657875236843760005b828110613d205750505050505050565b602082613d328261283085888c614072565b166001918282101580613e23575b15613dc657613d4f8288614126565b51613d69575090612b5c613d64939287614126565b613d10565b6064908a51907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601660248201527f4475706c6963617465206e756d62657220666f756e64000000000000000000006044820152fd5b6064908a51907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601360248201527f4e756d626572206f7574206f662072616e6765000000000000000000000000006044820152fd5b5087821115613d40565b82613e39828989614072565b3510613ebd5781613e4b828989614072565b3511613e5f57613e5a90614045565b613cdf565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f416d6f756e742065786365656473206d617800000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f416d6f756e742062656c6f77206d696e696d756d0000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f43686f6963657320657863656564206e756d6265722072616e676500000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4e6f2063686f696365732070726f7669646564000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e76616c6964206e756d6265722072616e67650000000000000000000000006044820152fd5b60a81c1683111590508138613cc2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146115be5760010190565b9190811015613a755760061b0190565b6060810190811067ffffffffffffffff8211176113f657604052565b610160810190811067ffffffffffffffff8211176113f657604052565b6080810190811067ffffffffffffffff8211176113f657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113f657604052565b3560ff811681036104a65790565b90610100811015613a755760051b0190565b1561413f57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b156141a457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b919082018092116115be57565b908160209103126104a6575180151581036104a65790565b818102929181159184041417156115be57565b919082039182116115be57565b1561424e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152fd5b67ffffffffffffffff81116113f65760051b60200190565b8051821015613a755760209160051b010190565b906040516142e581614082565b604060ff600283958054855260018101546020860152015416910152565b73ffffffffffffffffffffffffffffffffffffffff90929192169160008381526012906020918083526040928383205495868610156143f4576143469086614202565b958087116143ec575b5061435a858761423a565b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061439d6143888a6142ac565b9961439588519b8c6140d7565b808b526142ac565b0136838a0137855b8781106143b757505050505050505090565b6143e7908286528484526143cd81888820613a5d565b90549060031b1c612b5c6143e18a8461423a565b8c6142c4565b6143a5565b95503861434f565b5083519650919450508401915067ffffffffffffffff82118483101761441e575280825236813790565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b1561445257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152fdfea164736f6c6343000813000a000000000000000000000000333d978efa83687f133db145e3c6e4a25a0ec32e000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd00000000000000000000000000000000000000000000d3c21bcecceda100000000000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c1f2e2cbef8ac2082b785a35cf819dea9fab7f7b0000000000000000000000000000000000000000000000000000000000001a0b

Deployed ByteCode

0x608080604052600436101561001357600080fd5b60003560e01c9081630484a22f146137a9575080631135daeb1461376e57806314558fab146137325780631c91bed7146136b1578063266f16a11461360f57806335ab5f64146135d35780633644e515146106f15780633e61e9dc1461356e5780633f4ba83a14613460578063431c73bd146132b65780634e0856a71461327a578063532f845d1461321c57806356a1c7011461312d578063577632c014613114578063595fc101146130bf5780635b3a8a2e14612dd15780635c975abb14612d8d5780636456110314612d5157806364df049e14612cff578063699fa50f14612cc3578063715018a614612c23578063737500e11461216d57806374958e35146121315780637517f5ff14611ff3578063774aa16814611fb75780637a58073614611f005780638456cb5914611e355780638571c44114611c935780638da5cb5b14611c4157806395ccea6714611ab35780639e905ac114611a5a578063a284b6d314611a1e578063a3999356146118f3578063a426e4c81461180f578063a6d64abf146117c3578063b2457e7614610db2578063c6b91f4b14610d70578063c7c4a61514610c98578063c7d77b1114610c5c578063d0ef024a14610bed578063d47afe1314610b94578063d6e6eb9f14610b58578063d8f4fa5e146109ff578063dc46c2411461095a578063e829482814610854578063ec5aa5381461080f578063ecb3c3af146106f6578063ed24911d146106f1578063f02138bb146106af578063f2fde38b14610575578063f526e39a14610539578063f74d5480146104e7578063fb64bd6a146104ab5763ffdb16521461027157600080fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760006101406040516102b18161409e565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201528261012082015201526004356000526010602052604060002060ff60076040519261030f8461409e565b73ffffffffffffffffffffffffffffffffffffffff815416845260018101546020850152600281015460408501526003810154606085015260048101546080850152600581015460a0850152600681015460c0850152015481811660e0840152818160081c16610100840152818160101c16151561012084015260181c161515610140820152600435600052601160205260406000209081546103b1816142ac565b926103bf60405194856140d7565b818452602084019060005260206000206000915b838310610488576101408561048488604051938373ffffffffffffffffffffffffffffffffffffffff8695511685526020810151602086015260408101516040860152606081015160608601526080810151608086015260a081015160a086015260c081015160c086015260ff60e08201511660e086015260ff6101008201511661010086015261012081015115156101208601520151151561014084015261018080610160850152830190613aa4565b0390f35b60036020600192610498856142d8565b8152019201920191906103d3565b600080fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600354604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600f54604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576105ac613a06565b6000549073ffffffffffffffffffffffffffffffffffffffff808316916105d4338414613c42565b1691821561062b577fffffffffffffffffffffffff0000000000000000000000000000000000000000839116176000557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60065460a81c16604051908152f35b613afc565b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65761072d613b89565b60ff60065491169060ff8160a01c1682101590816107fe575b50156107a057801561077157600a546040805161271084900481526020810193909352820152606090f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f496e76616c69642072616e6765000000000000000000000000000000000000006044820152fd5b60ff915060a81c1681111582610746565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602061084c60055442614202565b604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576004356108a973ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8015158061094e575b156108f05760407f10e19a1a714c086ca5710ed885ba6097611170f0d62b2fe5f97e586f6ea7318591600a549080600a5582519182526020820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e76616c6964205254502070657263656e74000000000000000000000000006044820152fd5b506127108111156108b2565b346104a65761096836613bca565b909160009182919061097b818684613ca7565b600091600a54915b80841061099a576040868682519182526020820152f35b909192946109b4906109ad87848a614072565b3590614202565b946127106109da856109d56109ca85878d614072565b3560ff881690614227565b614227565b048581116109f5575b506109ed90614045565b929190610983565b94506109ed6109e3565b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657600435610a5473ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8015610afa57610e108111610a9c5760407f576363f65c117fad2e20056ea0e05d0b42d33a4bd5507b89591b0ab732b6878091600554908060055582519182526020820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f446561646c696e6520746f6f206c6f6e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420646561646c696e65206475726174696f6e000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600854604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517f696dd1dfe35e9837d052c32a94268b1e30279d02e5abdc07018dc2a61f4fe1128152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd168152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600a54604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576004356000526010602052610160604060002060ff73ffffffffffffffffffffffffffffffffffffffff82541691600181015490600281015460038201546004830154906005840154926007600686015495015495604051988952602089015260408801526060870152608086015260a085015260c084015281811660e0840152818160081c16610100840152818160101c16151561012084015260181c161515610140820152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60065460a01c16604051908152f35b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657610df06002600154141561419d565b60026001556004356000526010602052604060002073ffffffffffffffffffffffffffffffffffffffff81541680156117655760078201549060ff8260101c1661170757610e476002840154600685015490614202565b804311156116a95761010081018082116115be57431161164b57409182156115ed5760ff1691600384015460405191602083019384526004356040840152606083015283608083015260a082015260a0815260c0810181811067ffffffffffffffff8211176113f65760405251902081156107715706600181018091116115be576007820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff1661ff00600884901b1617620100001790556004356000908152601160205260408120909290835b815481101561152d57610f2a8183613c26565b5060ff60028187169201541614610f4a575b610f4590614045565b610f17565b610f548183613c26565b50610f7a612710610f6b8354600588015490614227565b0460ff60078701541690614227565b906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165afa80156113095784916000916114f8575b508381106114a257505061271061100f60075484614227565b04906110a8602061271061102560085487614227565b049285600161103d86611038898561423a565b61423a565b92600d61104b848254614202565b9055015588546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481019190915291829081906044820190565b0381600073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165af190811561130957600091611483575b50156114255781611315575b80611199575b604080518481526020810193909352820152600435907f8891369dd1f0ce0c069cb737b0a2ed608f94382ccad96d5fe2d7630170fc655d9080606081015b0390a25b80611154575b50610f3c565b61116290610f459296614202565b6007840180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff166301000000179055949061114e565b6009546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024810182905260208180604481010381600073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165af1908115611309576000916112da575b501561127c576111447f8891369dd1f0ce0c069cb737b0a2ed608f94382ccad96d5fe2d7630170fc655d91600f611272828254614202565b9055915050611106565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f50726f746f636f6c20666565206661696c6564000000000000000000000000006044820152fd5b6112fc915060203d602011611302575b6112f481836140d7565b81019061420f565b8961123a565b503d6112ea565b6040513d6000823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd163b156104a6576040517f42966c680000000000000000000000000000000000000000000000000000000081528260048201526000816024818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165af18015611309576113de575b50600e6113d7838254614202565b9055611100565b67ffffffffffffffff81116113f657604052886113c9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f526577617264207472616e73666572206661696c6564000000000000000000006044820152fd5b61149c915060203d602011611302576112f481836140d7565b896110f4565b91509173ffffffffffffffffffffffffffffffffffffffff8654169160405191825260208201527f1f9cd3d952b325a18cdca37b5aea2f219d8375edb61c9fa2a6e467e45fcebd0a604060043592a46000611148565b9150506020813d602011611525575b81611514602093836140d7565b810103126104a65783905189610ff6565b3d9150611507565b5060ff90849283600482015573ffffffffffffffffffffffffffffffffffffffff815416938360078301549354966005600185015494015493604051988387168a5260208a01526040890152166060870152608086015260a085015260181c161515917f3cdef9cb42e9d23d0fde40055cad23a024c0f73e8e7a5f125682946fda31dbd960c060043592a460018055005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f426c6f636b206861736820756e617661696c61626c65000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f52657665616c2077696e646f77206578706972656400000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f546f6f206561726c7920746f2072657665616c000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f416c72656164792072657665616c6564000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f436f6d6d6974206e6f7420666f756e64000000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657604060065460ff825191818160a01c16835260a81c166020820152f35b346104a65761181d36613b55565b60006040805161182c81614082565b82815282602082015201528160005260116020526040600020548110156118955761186961186f9160609360005260116020526040600020613c26565b506142d8565b611893604051809260ff604080928051855260208101516020860152015116910152565bf35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5469636b657420696e646578206f7574206f6620626f756e64730000000000006044820152fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043561194873ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b80151580611a13575b1561198f5760407f516783205ee62f5388530b90857153150ad3f46cd64a2029d165ecdc5254c71a91600454908060045582519182526020820152a1005b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f426c6f636b206f6666736574206d75737420626520706f73697469766520616e60448201527f64206c657373207468616e2032353600000000000000000000000000000000006064820152fd5b506101008110611951565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600254604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517fef643a4aacdbb61aec2490bfc65cdfbf19319dc39c69cf404da063f49286362e8152f35b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657611aea613a06565b73ffffffffffffffffffffffffffffffffffffffff611b0e81600054163314613c42565b611b1d6002600154141561419d565b600260015580821615611be3576040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024803590830152602090829060449082906000907f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165af1801561130957611bbf91600091611bc5575b5061444b565b60018055005b611bdd915060203d8111611302576112f481836140d7565b82611bb9565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043590611cd66002600154141561419d565b60026001556040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd1681836064816000855af190811561130957611d728392602495600091611e18575061444b565b604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa91821561130957600092611de9575b506040519283528201527f09c1e511cc9e2b5b245d0f85ff8a151b74969dbc1fb73f98973df6e8df1950ee60403392a260018055005b9080925081813d8311611e11575b611e0181836140d7565b810103126104a657519083611db3565b503d611df7565b611e2f9150843d8611611302576112f481836140d7565b87611bb9565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600054611ebf3373ffffffffffffffffffffffffffffffffffffffff831614613c42565b611ecf60ff8260a01c1615614138565b16176000557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346104a657611f0e36613b55565b90611f3273ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b6103e8808211159081611fac575b5015611f4e57600755600855005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f46656520746f6f206869676800000000000000000000000000000000000000006044820152fd5b905082111583611f40565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600454604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd165afa8015611309576000906120fe575b61014090600454600b54600c5460ff600d54600654906002549360035495600a5497604051998a5260208a0152604089015260608801526080870152818160a01c1660a087015260a81c1660c085015260e0840152610100830152610120820152f35b506020813d8211612129575b81612117602093836140d7565b810103126104a657610140905161209b565b3d915061210a565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600d54604051908152f35b346104a65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576121a4613b89565b60243567ffffffffffffffff81116104a6576121c4903690600401613b99565b9067ffffffffffffffff60a435116104a65736602360a4350112156104a65767ffffffffffffffff60a43560040135116104a65736602460a4356004013560a4350101116104a65761221e60ff60005460a01c1615614138565b61222d6002600154141561419d565b600260015561223d828285613ca7565b6084354211612bc5573360005260136020526064356040600020541015612b6757612267826142ac565b61227460405191826140d7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06122a1846142ac565b0136602083013760005b838110612ae9575060405160208101818193602081519391019260005b818110612ad05750506123029250037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826140d7565b519020604051907f696dd1dfe35e9837d052c32a94268b1e30279d02e5abdc07018dc2a61f4fe112602083015233604083015260ff85166060830152608082015260443560a082015260643560c082015260843560e082015260e081528061010081011067ffffffffffffffff610100830111176113f657610100810160405261010081516020830120916101208101927f190100000000000000000000000000000000000000000000000000000000000084527f506d9409017c5113dd34414e8d2050ff40843a84a15d1417d0ec1a3a94b580116101228301526101428201526042828201526123f48282016140bb565b01519020604160a4356004013503612a7257601b606460a435013560001a148015612a5f575b15612a01577f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0604460a4350135116129a35760006080602092604051908152606460a4350135831a84820152602460a43501356040820152604460a4350135606082015282805260015afa15611309576124d073ffffffffffffffffffffffffffffffffffffffff600051166124b1811515614247565b73ffffffffffffffffffffffffffffffffffffffff6006541614614247565b3360005260136020526064356040600020556124ed600c54614045565b9182600c5560009160005b82811061298557506040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810184905260208160648160007f000000000000000000000000abccefb00528c9c792ac7c46997f0f6ee5dcdddd73ffffffffffffffffffffffffffffffffffffffff165af190811561130957600091612966575b50156129085760005b828110612811575050600a54600454604051916125ae8361409e565b33835284602084015243604084015260443560608401526000608084015260a083015260c082015260ff851660e08201526000610100820152600061012082015260006101408201528360005260106020526007604060002073ffffffffffffffffffffffffffffffffffffffff8351167fffffffffffffffffffffffff00000000000000000000000000000000000000008254161781556020830151600182015560408301516002820155606083015160038201556080830151600482015560a0830151600582015560c08301516006820155019060ff60e0820151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008354161782556126f360ff6101008301511683907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff0083549260081b169116179055565b6101208101511515907fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff62ff000063ff0000006101408654940151151560181b169360101b1691161717905533600052601260205260406000208054680100000000000000008110156113f65761276f91600182018155613a5d565b81549060031b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86831b921b19161790556127ad81600b54614202565b600b55600454916127be8343614202565b90600a549260ff60405197168752602087015260408601526060850152608084015260a08301527f556b5f8979ea10226082d75e6b6750390a02c97eeed3a6588b31f2d8ed3051ff60c03393a360018055005b8460005260116020526040600020906128366020612830838787614072565b01614118565b9160ff612844838787614072565b35936040519461285386614082565b8552600060208601521660408401528054680100000000000000008110156113f65761288491600182018155613c26565b9290926128d95760ff604060026128d495845181556020850151600182015501920151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055614045565b612592565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5061796d656e74206661696c65640000000000000000000000000000000000006044820152fd5b61297f915060203d602011611302576112f481836140d7565b86612589565b9261299861299e916109ad868686614072565b93614045565b6124f8565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c696420732076616c756500000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c696420762076616c756500000000000000000000000000000000006044820152fd5b50601c606460a435013560001a1461241a565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c6964207369676e6174757265206c656e67746800000000000000006044820152fd5b84518352602094850194869450909201916001016122c8565b80612af8612b62928686614072565b35612b096020612830848989614072565b6040519060208201927fef643a4aacdbb61aec2490bfc65cdfbf19319dc39c69cf404da063f49286362e8452604083015260ff60609116818301528152612b4f816140bb565b519020612b5c82856142c4565b52614045565b6122ab565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4e6f6e636520616c7265616479207573656400000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f5369676e617475726520657870697265640000000000000000000000000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657600080547fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff821691612c9a338414613c42565b1682557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600554604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602073ffffffffffffffffffffffffffffffffffffffff60095416604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600c54604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657602060ff60005460a01c166040519015158152f35b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657612e08613b89565b60249081359160ff808416928385036104a657612e3e73ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b8181169260028410613061578285116130035784841015612fa557838503838111612f7757836001911610612f19577fe9f01c17475d67901c9556d8c8538ad113af4128eadd382ab327a2e91f0a20cf6080878787878774ff000000000000000000000000000000000000000075ff0000000000000000000000000000000000000000006006549660a81b169160a01b167fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff8616171760065560405193818160a01c16855260a81c16602084015260408301526060820152a1005b6064906014604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f52616e6765207370616e20746f6f20736d616c6c0000000000000000000000006044820152fd5b507f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6064906019604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d696e206d757374206265206c657373207468616e206d6178000000000000006044820152fd5b6064906013604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d61782072616e676520746f6f206c61726765000000000000000000000000006044820152fd5b606490601c604051917f08c379a0000000000000000000000000000000000000000000000000000000008352602060048401528201527f4d696e2072616e6765206d757374206265206174206c656173742032000000006044820152fd5b346104a6576130cd36613b55565b906000526011602052604060002080548210156104a6576060916130f091613c26565b5080549060ff60026001830154920154169060405192835260208301526040820152f35b346104a65761312b61312536613bca565b91613ca7565b005b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657613164613a06565b73ffffffffffffffffffffffffffffffffffffffff9061318982600054163314613c42565b1680156131be577fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655600080f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964207369676e65720000000000000000000000000000000000006044820152fd5b346104a65760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a657610484613266613259613a06565b6044359060243590614303565b604051918291602083526020830190613a29565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600754604051908152f35b346104a6576132c436613b55565b6132e773ffffffffffffffffffffffffffffffffffffffff600054163314613c42565b81156134025780156133a457808211613346577f6a49e336c3dd05f6936f44151964f6f99be0dd0f44e075566e0354b2928e225c91608091600354916002548160035582600255604051938452602084015260408301526060820152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4d696e20616d6f756e74206d757374206265203c3d206d617820616d6f756e746044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d617820616d6f756e74206d75737420626520706f73697469766500000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4d696e20616d6f756e74206d75737420626520706f73697469766500000000006044820152fd5b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576000546134b33373ffffffffffffffffffffffffffffffffffffffff831614613c42565b60ff8160a01c1615613510577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166000557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b346104a65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65773ffffffffffffffffffffffffffffffffffffffff6135ba613a06565b1660005260136020526020604060002054604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600e54604051908152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760043560005260118152604060002090815461365a816142ac565b9261366860405194856140d7565b8184526000908152828120838086015b84841061369257604051828152806104848185018a613aa4565b6001916003916136a1856142d8565b8152019201920191908490613678565b346104a65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576136e8613a06565b73ffffffffffffffffffffffffffffffffffffffff60243591166000526012602052604060002080548210156104a65760209161372491613a5d565b90546040519160031b1c8152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a6576020600b54604051908152f35b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040516127108152f35b346104a6576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65773ffffffffffffffffffffffffffffffffffffffff6137f6613a06565b1660005260128152816040600020805480835283830191600052836000209060005b8181106139ef578461387687613830838803846140d7565b82519261383c846142ac565b9361384a60405195866140d7565b8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09384916142ac565b018260005b8281106139df5750505060005b81518110156139225761389b81836142c4565b5160005260118352604060002080546138b3816142ac565b916138c160405193846140d7565b818352600090815285812090868085015b848310613903575050505050906138fe916138ed82886142c4565b526138f881876142c4565b50614045565b613888565b600191600391613912866142d8565b81520193019101909187906138d2565b5091929061393b60405193604085526040850190613a29565b908382038185015282519182815281810182808560051b8401019501966000925b8584106139695787870388f35b90919293868383839899030185528951908280835192838152019201906000905b8082106139a757505050988101989695946001019301919061395c565b91936060846139d160019496885160ff604080928051855260208101516020860152015116910152565b01940192018893929161398a565b606082828901015201839061387b565b825484528694509285019260019283019201613818565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104a657565b90815180825260208080930193019160005b828110613a49575050505090565b835185529381019392810192600101613a3b565b8054821015613a755760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90815180825260208080930193019160005b828110613ac4575050505090565b9091929382606082613af0600194895160ff604080928051855260208101516020860152015116910152565b01950193929101613ab6565b346104a65760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a65760206040517f506d9409017c5113dd34414e8d2050ff40843a84a15d1417d0ec1a3a94b580118152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126104a6576004359060243590565b6004359060ff821682036104a657565b9181601f840112156104a65782359167ffffffffffffffff83116104a6576020808501948460061b0101116104a657565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104a65760043560ff811681036104a657916024359067ffffffffffffffff82116104a657613c2291600401613b99565b9091565b8054821015613a75576000526003602060002091020190600090565b15613c4957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b6006549060ff8091169181808260a01c168410159182614035575b505015613fd7578315613f7957818411613f1b5760035460025460005b868110613e2d5750505060409384519161200080840184811067ffffffffffffffff8211176113f657875236843760005b828110613d205750505050505050565b602082613d328261283085888c614072565b166001918282101580613e23575b15613dc657613d4f8288614126565b51613d69575090612b5c613d64939287614126565b613d10565b6064908a51907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601660248201527f4475706c6963617465206e756d62657220666f756e64000000000000000000006044820152fd5b6064908a51907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601360248201527f4e756d626572206f7574206f662072616e6765000000000000000000000000006044820152fd5b5087821115613d40565b82613e39828989614072565b3510613ebd5781613e4b828989614072565b3511613e5f57613e5a90614045565b613cdf565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f416d6f756e742065786365656473206d617800000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f416d6f756e742062656c6f77206d696e696d756d0000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f43686f6963657320657863656564206e756d6265722072616e676500000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4e6f2063686f696365732070726f7669646564000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e76616c6964206e756d6265722072616e67650000000000000000000000006044820152fd5b60a81c1683111590508138613cc2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146115be5760010190565b9190811015613a755760061b0190565b6060810190811067ffffffffffffffff8211176113f657604052565b610160810190811067ffffffffffffffff8211176113f657604052565b6080810190811067ffffffffffffffff8211176113f657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113f657604052565b3560ff811681036104a65790565b90610100811015613a755760051b0190565b1561413f57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b156141a457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b919082018092116115be57565b908160209103126104a6575180151581036104a65790565b818102929181159184041417156115be57565b919082039182116115be57565b1561424e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152fd5b67ffffffffffffffff81116113f65760051b60200190565b8051821015613a755760209160051b010190565b906040516142e581614082565b604060ff600283958054855260018101546020860152015416910152565b73ffffffffffffffffffffffffffffffffffffffff90929192169160008381526012906020918083526040928383205495868610156143f4576143469086614202565b958087116143ec575b5061435a858761423a565b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061439d6143888a6142ac565b9961439588519b8c6140d7565b808b526142ac565b0136838a0137855b8781106143b757505050505050505090565b6143e7908286528484526143cd81888820613a5d565b90549060031b1c612b5c6143e18a8461423a565b8c6142c4565b6143a5565b95503861434f565b5083519650919450508401915067ffffffffffffffff82118483101761441e575280825236813790565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b1561445257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152fdfea164736f6c6343000813000a