Address Contract Partially Verified
Address
0x0822465a4Ab614bcC53Efc4AA426729bF5D4C65f
Balance
0 ETH
Nonce
1
Code Size
8321 bytes
Creator
0x51Ad709f...D50c at tx 0x62e60bb7...a68b6e
Indexed Transactions
0
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