Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x0822465a4Ab614bcC53Efc4AA426729bF5D4C65f
Balance 0 ETH
Nonce 1
Code Size 8321 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

8321 bytes
0x608060405234801561000f575f5ffd5b506004361061011f575f3560e01c806372d3c2fd116100ab578063b10727b71161006f578063b10727b714610319578063bd7fa2c314610349578063c57667e914610379578063ef410f4514610395578063f2fde38b146103b35761011f565b806372d3c2fd1461024d5780637ef8526f1461027d5780638409a4c2146102ad5780638da5cb5b146102cb578063ac68b9cb146102e95761011f565b8063310649cf116100f2578063310649cf146101bb578063330af514146101d95780634bb1dbcd146101f55780635e8c27be14610225578063715018a6146102435761011f565b806313e91c5614610123578063198c1b81146101535780632e1a7d4d146101835780632f52ebb71461019f575b5f5ffd5b61013d6004803603810190610138919061158d565b6103cf565b60405161014a91906115d2565b60405180910390f35b61016d6004803603810190610168919061158d565b6103eb565b60405161017a91906115fa565b60405180910390f35b61019d6004803603810190610198919061158d565b610400565b005b6101b960048036038101906101b49190611674565b6105c6565b005b6101c3610a24565b6040516101d0919061174b565b60405180910390f35b6101f360048036038101906101ee9190611764565b610a49565b005b61020f600480360381019061020a9190611674565b610a67565b60405161021c91906115fa565b60405180910390f35b61022d610b25565b60405161023a91906115d2565b60405180910390f35b61024b610b37565b005b6102676004803603810190610262919061158d565b610b4a565b60405161027491906117c7565b60405180910390f35b61029760048036038101906102929190611674565b610b6a565b6040516102a491906115fa565b60405180910390f35b6102b5610bc2565b6040516102c291906117c7565b60405180910390f35b6102d3610bc8565b6040516102e09190611800565b60405180910390f35b61030360048036038101906102fe9190611674565b610bef565b60405161031091906115d2565b60405180910390f35b610333600480360381019061032e9190611674565b610cbd565b60405161034091906115d2565b60405180910390f35b610363600480360381019061035e919061158d565b610d3f565b60405161037091906115fa565b60405180910390f35b610393600480360381019061038e919061158d565b610d59565b005b61039d6111bf565b6040516103aa9190611839565b60405180910390f35b6103cd60048036038101906103c8919061187c565b6111e4565b005b5f5f60035f8481526020019081526020015f2054119050919050565b6003602052805f5260405f205f915090505481565b610408611268565b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016104639190611800565b602060405180830381865afa15801561047e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a291906118bb565b9050808211156104e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104de90611940565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33846040518363ffffffff1660e01b815260040161054392919061195e565b6020604051808303815f875af115801561055f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061058391906119af565b6105c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105b990611a24565b60405180910390fd5b5050565b3373ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636352211e856040518263ffffffff1660e01b815260040161063791906115fa565b602060405180830381865afa158015610652573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106769190611a56565b73ffffffffffffffffffffffffffffffffffffffff16146106cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c390611acb565b60405180910390fd5b5f6106d8848484610cbd565b90505f6106e6858585610bef565b9050610d25851115806106f65750815b806106fe5750805b61073d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073490611b59565b60405180910390fd5b5f60035f8781526020019081526020015f205490505f8111156107a0575f858590501161079f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161079690611bc1565b60405180910390fd5b5b5f6107ac8785856112ef565b90508181116107f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107e790611c4f565b60405180910390fd5b5f82826107fd9190611c9a565b90508060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161085a9190611800565b602060405180830381865afa158015610875573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061089991906118bb565b10156108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190611940565b60405180910390fd5b8160035f8a81526020019081526020015f208190555060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b815260040161094c92919061195e565b6020604051808303815f875af1158015610968573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061098c91906119af565b6109cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c290611d17565b60405180910390fd5b873373ffffffffffffffffffffffffffffffffffffffff167f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf783604051610a1291906115fa565b60405180910390a35050505050505050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610a51611268565b818160059190610a629291906114ec565b505050565b5f5f3073ffffffffffffffffffffffffffffffffffffffff16637ef8526f8686866040518463ffffffff1660e01b8152600401610aa693929190611dad565b602060405180830381865afa158015610ac1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ae591906118bb565b90505f60035f8781526020019081526020015f20549050808211610b0d575f92505050610b1e565b8082610b199190611c9a565b925050505b9392505050565b60065f9054906101000a900460ff1681565b610b3f611268565b610b485f61137f565b565b60058181548110610b59575f80fd5b905f5260205f20015f915090505481565b5f5f610b77858585610cbd565b90505f610b85868686610bef565b9050610d2586111580610b955750815b80610b9d5750805b610bab575f92505050610bbb565b610bb68683836112ef565b925050505b9392505050565b60045481565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f5f84604051602001610c0291906115fa565b6040516020818303038152906040528051906020012090505f5f90505b600580549050811015610cb057610c938585808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f8201169050808301925050505050505060058381548110610c8457610c83611ddd565b5b905f5260205f20015484611440565b15610ca357600192505050610cb6565b8080600101915050610c1f565b505f9150505b9392505050565b5f5f84604051602001610cd091906115fa565b604051602081830303815290604052805190602001209050610d358484808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f8201169050808301925050505050505060045483611440565b9150509392505050565b5f60035f8381526020019081526020015f20549050919050565b60065f9054906101000a900460ff1615610da8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d9f90611e54565b60405180910390fd5b7f66925e85f1a4743fd8d60ba595ed74887b7caf321dd83b21e04d77c1153834085f1b81604051602001610ddc9190611e92565b6040516020818303038152906040528051906020012014610e32576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2990611ef6565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636352211e836040518263ffffffff1660e01b8152600401610ea391906115fa565b602060405180830381865afa158015610ebe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ee29190611a56565b73ffffffffffffffffffffffffffffffffffffffff1614610f38576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f2f90611f5e565b60405180910390fd5b5f60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610f939190611800565b602060405180830381865afa158015610fae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd291906118bb565b90505f670de0b6b3a7640000600a84610feb9190611f7c565b610ff59190611f7c565b90508181111561103a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110319061202d565b60405180910390fd5b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b815260040161109692919061195e565b6020604051808303815f875af11580156110b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110d691906119af565b611115576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161110c90611d17565b60405180910390fd5b600160065f6101000a81548160ff02191690831515021790555060057f05048a079a07ef7749db7bd397d7811e0ad1b93ba9264746543fc36465176e85908060018154018082558091505060019003905f5260205f20015f90915f1b9091909150553373ffffffffffffffffffffffffffffffffffffffff167f86058396a71dcce61767a39f0de90a66f551604cf9d834440d6b1084a8077f9c60405160405180910390a2505050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6111ec611268565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361125c575f6040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016112539190611800565b60405180910390fd5b6112658161137f565b50565b611270611456565b73ffffffffffffffffffffffffffffffffffffffff1661128e610bc8565b73ffffffffffffffffffffffffffffffffffffffff16146112ed576112b1611456565b6040517f118cdaa70000000000000000000000000000000000000000000000000000000081526004016112e49190611800565b60405180910390fd5b565b5f5f600a90505f8190506064861161131057818161130d9190611f7c565b90505b83156113255781816113229190611f7c565b90505b84156113485781816113379190611f7c565b905081816113459190611f7c565b90505b610d25861161136057818161135d9190611f7c565b90505b670de0b6b3a7640000816113749190611f7c565b925050509392505050565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f8261144c858461145d565b1490509392505050565b5f33905090565b5f5f8290505f5f90505b84518110156114a3576114948286838151811061148757611486611ddd565b5b60200260200101516114ae565b91508080600101915050611467565b508091505092915050565b5f8183106114c5576114c082846114d8565b6114d0565b6114cf83836114d8565b5b905092915050565b5f825f528160205260405f20905092915050565b828054828255905f5260205f20908101928215611526579160200282015b8281111561152557823582559160200191906001019061150a565b5b5090506115339190611537565b5090565b5b8082111561154e575f815f905550600101611538565b5090565b5f5ffd5b5f5ffd5b5f819050919050565b61156c8161155a565b8114611576575f5ffd5b50565b5f8135905061158781611563565b92915050565b5f602082840312156115a2576115a1611552565b5b5f6115af84828501611579565b91505092915050565b5f8115159050919050565b6115cc816115b8565b82525050565b5f6020820190506115e55f8301846115c3565b92915050565b6115f48161155a565b82525050565b5f60208201905061160d5f8301846115eb565b92915050565b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f84011261163457611633611613565b5b8235905067ffffffffffffffff81111561165157611650611617565b5b60208301915083602082028301111561166d5761166c61161b565b5b9250929050565b5f5f5f6040848603121561168b5761168a611552565b5b5f61169886828701611579565b935050602084013567ffffffffffffffff8111156116b9576116b8611556565b5b6116c58682870161161f565b92509250509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f819050919050565b5f61171361170e611709846116d1565b6116f0565b6116d1565b9050919050565b5f611724826116f9565b9050919050565b5f6117358261171a565b9050919050565b6117458161172b565b82525050565b5f60208201905061175e5f83018461173c565b92915050565b5f5f6020838503121561177a57611779611552565b5b5f83013567ffffffffffffffff81111561179757611796611556565b5b6117a38582860161161f565b92509250509250929050565b5f819050919050565b6117c1816117af565b82525050565b5f6020820190506117da5f8301846117b8565b92915050565b5f6117ea826116d1565b9050919050565b6117fa816117e0565b82525050565b5f6020820190506118135f8301846117f1565b92915050565b5f6118238261171a565b9050919050565b61183381611819565b82525050565b5f60208201905061184c5f83018461182a565b92915050565b61185b816117e0565b8114611865575f5ffd5b50565b5f8135905061187681611852565b92915050565b5f6020828403121561189157611890611552565b5b5f61189e84828501611868565b91505092915050565b5f815190506118b581611563565b92915050565b5f602082840312156118d0576118cf611552565b5b5f6118dd848285016118a7565b91505092915050565b5f82825260208201905092915050565b7f496e73756666696369656e7420636f6e74726163742062616c616e63650000005f82015250565b5f61192a601d836118e6565b9150611935826118f6565b602082019050919050565b5f6020820190508181035f8301526119578161191e565b9050919050565b5f6040820190506119715f8301856117f1565b61197e60208301846115eb565b9392505050565b61198e816115b8565b8114611998575f5ffd5b50565b5f815190506119a981611985565b92915050565b5f602082840312156119c4576119c3611552565b5b5f6119d18482850161199b565b91505092915050565b7f5769746864726177206661696c656400000000000000000000000000000000005f82015250565b5f611a0e600f836118e6565b9150611a19826119da565b602082019050919050565b5f6020820190508181035f830152611a3b81611a02565b9050919050565b5f81519050611a5081611852565b92915050565b5f60208284031215611a6b57611a6a611552565b5b5f611a7884828501611a42565b91505092915050565b7f596f7520646f6e2774206f776e2074686973206b6974747900000000000000005f82015250565b5f611ab56018836118e6565b9150611ac082611a81565b602082019050919050565b5f6020820190508181035f830152611ae281611aa9565b9050919050565b7f6b69747479206973206e6f7420656c696769626c6520666f72207468697320635f8201527f6c61696d00000000000000000000000000000000000000000000000000000000602082015250565b5f611b436024836118e6565b9150611b4e82611ae9565b604082019050919050565b5f6020820190508181035f830152611b7081611b37565b9050919050565b7f5265636c61696d73206d75737420696e636c75646520612070726f6f660000005f82015250565b5f611bab601d836118e6565b9150611bb682611b77565b602082019050919050565b5f6020820190508181035f830152611bd881611b9f565b9050919050565b7f54686973206b697474792068617320616c726561647920636c61696d656420745f8201527f6865206d6178696d756d20616d6f756e74000000000000000000000000000000602082015250565b5f611c396031836118e6565b9150611c4482611bdf565b604082019050919050565b5f6020820190508181035f830152611c6681611c2d565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611ca48261155a565b9150611caf8361155a565b9250828203905081811115611cc757611cc6611c6d565b5b92915050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f611d01600f836118e6565b9150611d0c82611ccd565b602082019050919050565b5f6020820190508181035f830152611d2e81611cf5565b9050919050565b5f82825260208201905092915050565b5f5ffd5b82818337505050565b5f611d5d8385611d35565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115611d9057611d8f611d45565b5b602083029250611da1838584611d49565b82840190509392505050565b5f604082019050611dc05f8301866115eb565b8181036020830152611dd3818486611d52565b9050949350505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f506f7274616c20616c7265616479206f70656e656400000000000000000000005f82015250565b5f611e3e6015836118e6565b9150611e4982611e0a565b602082019050919050565b5f6020820190508181035f830152611e6b81611e32565b9050919050565b5f819050919050565b611e8c611e878261155a565b611e72565b82525050565b5f611e9d8284611e7b565b60208201915081905092915050565b7f54686973206b697474792063616e206e6f74206f70656e20706f7274616c73005f82015250565b5f611ee0601f836118e6565b9150611eeb82611eac565b602082019050919050565b5f6020820190508181035f830152611f0d81611ed4565b9050919050565b7f506f7274616c206d757374206265206f70656e6564206279206f776e657200005f82015250565b5f611f48601e836118e6565b9150611f5382611f14565b602082019050919050565b5f6020820190508181035f830152611f7581611f3c565b9050919050565b5f611f868261155a565b9150611f918361155a565b9250828202611f9f8161155a565b91508282048414831517611fb657611fb5611c6d565b5b5092915050565b7f4d6f7265202450555252206e656564656420746f20706f7765722074686520705f8201527f6f7274616c210000000000000000000000000000000000000000000000000000602082015250565b5f6120176026836118e6565b915061202282611fbd565b604082019050919050565b5f6020820190508181035f8301526120448161200b565b905091905056fea2646970667358221220742da749468f7a20c600c4f80e169eaa7bfb8c3ce383ce92cddc8bed5673856e64736f6c634300081e0033

Verified Source Code Partial Match

Compiler: v0.8.30+commit.73712a01 EVM: prague Optimization: No
PurrClaim.sol 980 lines
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol


// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

// File: @openzeppelin/contracts/utils/Context.sol


// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;


/**
 * @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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/cryptography/Hashes.sol


// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/Hashes.sol)

pragma solidity ^0.8.20;

/**
 * @dev Library of standard hash functions.
 *
 * _Available since v5.1._
 */
library Hashes {
    /**
     * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
     *
     * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
     */
    function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
        return a < b ? efficientKeccak256(a, b) : efficientKeccak256(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function efficientKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
        assembly ("memory-safe") {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// File: @openzeppelin/contracts/utils/cryptography/MerkleProof.sol


// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MerkleProof.sol)
// This file was procedurally generated from scripts/generate/templates/MerkleProof.js.

pragma solidity ^0.8.20;


/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 *
 * IMPORTANT: Consider memory side-effects when using custom hashing functions
 * that access memory in an unsafe way.
 *
 * NOTE: This library supports proof verification for merkle trees built using
 * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
 * leaf inclusion in trees built using non-commutative hashing functions requires
 * additional logic that is not supported by this library.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProof(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function processProof(
        bytes32[] memory proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProofCalldata(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function processProofCalldata(
        bytes32[] calldata proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = Hashes.commutativeKeccak256(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProof(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = hasher(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = Hashes.commutativeKeccak256(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = hasher(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }
}

// File: contracts/PurrClaim.sol


pragma solidity ^0.8.0;




interface ICryptoKitties {
    function ownerOf(uint256 tokenId) external view returns (address);
}

contract PurrClaim is Ownable {
    IERC20 public purrToken;
    ICryptoKitties public cryptoKitties;
    
    mapping(uint256 => uint256) public kittyClaimed;
    bytes32 public diamonds;
    bytes32[] public exclusives;
    bool public portalOpen;

    event Claim(address indexed claimer, uint256 indexed kittyId, uint256 amount);
    event PortalOpen(address indexed openedBy);

    constructor(address _purrToken, address _cryptoKitties) Ownable(msg.sender) {
        purrToken = IERC20(_purrToken);
        cryptoKitties = ICryptoKitties(_cryptoKitties);
        diamonds = 0x12349fca1989b4a32ad421092981473494e8d91675b639d881ff4141a9412f0a;
        exclusives.push(0x68be1c5b2727fbdc09c67656b2a7286a2a8d8dfd12ef2fbc893b51903106af5c);
        portalOpen = false;
    }
    
    /**
     * @dev Check if a kitty ID is a diamond kitty using a Merkle proof
     * @param kittyId The kitty ID to check
     * @param merkleProof The Merkle proof for the kitty ID
     * @return Whether the kitty is a diamond kitty
     */
    function isDiamond(uint256 kittyId, bytes32[] calldata merkleProof) public view returns (bool) {
        bytes32 leaf = keccak256(abi.encode(kittyId));
        return MerkleProof.verify(merkleProof, diamonds, leaf);
    }

    /**
     * @dev Check if a kitty ID is an exclusive using Merkle proofs
     * @param kittyId The kitty ID to check
     * @param proof The Merkle proof for the kitty ID
     * @return Whether the kitty is exclusive
     */
    function isExclusive(uint256 kittyId, bytes32[] calldata proof) public view returns (bool) {
        bytes32 leaf = keccak256(abi.encode(kittyId));
        for (uint256 i = 0; i < exclusives.length; i++) {
            if (MerkleProof.verify(proof, exclusives[i], leaf)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @dev Claim PURR tokens for a specific kitty
     * @param kittyId The ID of the kitty to claim for
     * @param proof The Merkle proof if claiming for a diamond kitty
     */
    function claim(uint256 kittyId, bytes32[] calldata proof) external {
        // Verify the sender owns this kitty
        require(cryptoKitties.ownerOf(kittyId) == msg.sender, "You don't own this kitty");

        bool diamond = isDiamond(kittyId, proof);
        bool exclusive = isExclusive(kittyId, proof);
        
        // Check if this kitty is eligible (either ID <= MAX_ELIGIBLE_ID or is a diamond kitty)
        require(
            kittyId <= 3365 || diamond || exclusive,
            "kitty is not eligible for this claim"
        );
        
        uint256 alreadyClaimed = kittyClaimed[kittyId];
        
        // For reclaims (kitty has already claimed something), require proof
        if (alreadyClaimed > 0) {
            require(proof.length > 0, "Reclaims must include a proof");
        }
        
        uint256 maxClaimable = calculatePurr(kittyId, diamond, exclusive);
        
        // Calculate the difference to claim
        require(maxClaimable > alreadyClaimed, "This kitty has already claimed the maximum amount");
        uint256 claimable = maxClaimable - alreadyClaimed;
        
        require(
            purrToken.balanceOf(address(this)) >= claimable,
            "Insufficient contract balance"
        );
        
        // Update the total claimed amount for this kitty
        kittyClaimed[kittyId] = maxClaimable;
        
        // Transfer tokens to the owner
        require(purrToken.transfer(msg.sender, claimable), "Transfer failed");
        emit Claim(msg.sender, kittyId, claimable);
    }

    /**
     * @dev Calculate the amount of PURR tokens to claim
     * @return Fixed amount of PURR tokens
     */
    function calculatePurr(uint256 kittyId, bool diamond, bool exclusive) internal pure returns (uint256) {
        uint256 baseAmount = 10;
        uint256 amount = baseAmount;

        if (kittyId <= 100) {
            amount *= baseAmount; // founders multiplier
        }

        if (exclusive) {
            amount *= baseAmount; // exclusives multiplier
        }

        if (diamond) {
            amount *= baseAmount; // diamonds multiplier
            amount *= baseAmount;
        }

        if (kittyId <= 3365) {
            amount *= baseAmount; // day1 multiplier
        }

        return amount * 10 ** 18;
    }

    function withdraw(uint256 amount) external onlyOwner {
        uint256 contractBalance = purrToken.balanceOf(address(this));
        require(amount <= contractBalance, "Insufficient contract balance");
        require(purrToken.transfer(msg.sender, amount), "Withdraw failed");
    }

    /**
     * @dev Update the entire array of Merkle roots for exclusives (onlyOwner)
     * @param roots The new array of Merkle roots for the exclusives batch
     */
    function updateExclusiveRoots(bytes32[] calldata roots) external onlyOwner {
        exclusives = roots; // Replace the entire exclusives array with the new one
    }

    /**
     * @dev Opens a portal so non-Day1 Exclusives can claim $PURR
     * @param kittyId a mystery kitty that can open a portal to a new dimension
     */
    function openPortal(uint256 kittyId) external {
        require(!portalOpen, "Portal already opened");
        require(keccak256(abi.encodePacked(kittyId)) == 0x66925e85f1a4743fd8d60ba595ed74887b7caf321dd83b21e04d77c115383408, "This kitty can not open portals");
        require(cryptoKitties.ownerOf(kittyId) == msg.sender, "Portal must be opened by owner");
        uint256 contractBalance = purrToken.balanceOf(address(this));
        uint256 amount = kittyId * 10 * 10 ** 18;
        require(amount <= contractBalance, "More $PURR needed to power the portal!");
        require(purrToken.transfer(msg.sender, amount), "Transfer failed");
        portalOpen = true;
        exclusives.push(0x05048a079a07ef7749db7bd397d7811e0ad1b93ba9264746543fc36465176e85);
        emit PortalOpen(msg.sender);
    }
    
    /**
     * @dev Check if a kitty has already claimed
     * @param kittyId The ID of the kitty to check
     * @return Whether the kitty has claimed
     */
    function hasKittyClaimed(uint256 kittyId) external view returns (bool) {
        return kittyClaimed[kittyId] > 0;
    }
    
    /**
     * @dev Get the amount a kitty has already claimed
     * @param kittyId The ID of the kitty to check
     * @return The amount already claimed by this kitty
     */
    function getKittyClaimedAmount(uint256 kittyId) external view returns (uint256) {
        return kittyClaimed[kittyId];
    }
    
    /**
     * @dev Get the maximum amount a kitty can claim with proper proofs
     * @param kittyId The ID of the kitty to check
     * @param proof The Merkle proof for diamond/exclusive verification
     * @return The maximum amount this kitty can claim
     */
    function getKittyMaxClaim(uint256 kittyId, bytes32[] calldata proof) external view returns (uint256) {
        // Check if this kitty is eligible
        bool diamond = isDiamond(kittyId, proof);
        bool exclusive = isExclusive(kittyId, proof);
        
        if (!(kittyId <= 3365 || diamond || exclusive)) {
            return 0; // Not eligible
        }
        
        return calculatePurr(kittyId, diamond, exclusive);
    }
    
    /**
     * @dev Get the remaining amount a kitty can still claim
     * @param kittyId The ID of the kitty to check
     * @param proof The Merkle proof for diamond/exclusive verification
     * @return The remaining amount this kitty can claim
     */
    function getKittyRemainingClaim(uint256 kittyId, bytes32[] calldata proof) external view returns (uint256) {
        uint256 maxClaim = this.getKittyMaxClaim(kittyId, proof);
        uint256 alreadyClaimed = kittyClaimed[kittyId];
        
        if (maxClaim <= alreadyClaimed) {
            return 0;
        }
        
        return maxClaim - alreadyClaimed;
    }
}

Read Contract

cryptoKitties 0xef410f45 → address
diamonds 0x8409a4c2 → bytes32
exclusives 0x72d3c2fd → bytes32
getKittyClaimedAmount 0xbd7fa2c3 → uint256
getKittyMaxClaim 0x7ef8526f → uint256
getKittyRemainingClaim 0x4bb1dbcd → uint256
hasKittyClaimed 0x13e91c56 → bool
isDiamond 0xb10727b7 → bool
isExclusive 0xac68b9cb → bool
kittyClaimed 0x198c1b81 → uint256
owner 0x8da5cb5b → address
portalOpen 0x5e8c27be → bool
purrToken 0x310649cf → address

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

claim 0x2f52ebb7
uint256 kittyId
bytes32[] proof
openPortal 0xc57667e9
uint256 kittyId
renounceOwnership 0x715018a6
No parameters
transferOwnership 0xf2fde38b
address newOwner
updateExclusiveRoots 0x330af514
bytes32[] roots
withdraw 0x2e1a7d4d
uint256 amount

Recent Transactions

No transactions found for this address