Description:
Smart contract deployed on Ethereum with Factory features.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/KushlingsClaim.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// --------- Minimal ERC721 interface ---------
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
}
// --------- Minimal MerkleProof (inline) ---------
library MerkleProof {
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == root;
}
}
// --------- Minimal Ownable ---------
abstract contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
require(initialOwner != address(0), "owner=0");
owner = initialOwner;
emit OwnershipTransferred(address(0), initialOwner);
}
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "owner=0");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// --------- Minimal ReentrancyGuard ---------
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() { _status = _NOT_ENTERED; }
modifier nonReentrant() {
require(_status != _ENTERED, "reentrancy");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
// =====================================================
// KUSHLINGS CLAIM CONTRACT
// =====================================================
contract KushlingsClaim is Ownable, ReentrancyGuard {
IERC721 public immutable nft;
address public immutable treasury;
uint256 public startId;
uint256 public endId;
uint256 public cursor;
bool public paused = true;
bytes32 public merkleRoot;
mapping(address => uint256) public claimed;
event Claimed(address indexed to, uint256 indexed tokenId);
event Paused(bool status);
event RangeSet(uint256 startId, uint256 endId);
event MerkleRootSet(bytes32 root);
error PausedErr();
error SoldOut();
error ExceedsAllowance(uint256 want, uint256 max);
error NotAllowlisted();
error BadRange();
constructor(
address _nft,
address _treasury,
uint256 _startId,
uint256 _endId,
bytes32 _merkleRoot,
address _owner
) Ownable(_owner) {
require(_nft != address(0) && _treasury != address(0), "bad addr");
require(_endId >= _startId, "range");
nft = IERC721(_nft);
treasury = _treasury;
startId = _startId;
endId = _endId;
cursor = _startId;
merkleRoot = _merkleRoot;
}
// ---------- admin ----------
function setPaused(bool _paused) external onlyOwner {
paused = _paused;
emit Paused(_paused);
}
function setRange(uint256 _startId, uint256 _endId) external onlyOwner {
require(paused, "pause first");
if (_endId < _startId) revert BadRange();
startId = _startId;
endId = _endId;
cursor = _startId;
emit RangeSet(_startId, _endId);
}
function setMerkleRoot(bytes32 _root) external onlyOwner {
merkleRoot = _root;
emit MerkleRootSet(_root);
}
// ---------- view ----------
function maxFor(address account, uint256 maxFromLeaf, bytes32[] calldata proof)
public
view
returns (uint256)
{
if (merkleRoot == 0) {
return type(uint256).max; // open claim
}
bytes32 leaf = keccak256(abi.encodePacked(account, maxFromLeaf));
if (!MerkleProof.verify(proof, merkleRoot, leaf)) revert NotAllowlisted();
return maxFromLeaf;
}
// ---------- claim ----------
function claim(uint256 qty, uint256 maxFromLeaf, bytes32[] calldata proof)
external
nonReentrant
{
if (paused) revert PausedErr();
require(qty > 0, "qty=0");
uint256 maxAllowed = maxFor(msg.sender, maxFromLeaf, proof);
uint256 newTotal = claimed[msg.sender] + qty;
if (newTotal > maxAllowed) revert ExceedsAllowance(qty, maxAllowed);
uint256 id = cursor;
unchecked {
for (uint256 i = 0; i < qty; i++) {
// find next NFT actually owned by treasury
while (id <= endId && nft.ownerOf(id) != treasury) {
id++;
}
if (id > endId) revert SoldOut();
// transfer out
nft.safeTransferFrom(treasury, msg.sender, id);
emit Claimed(msg.sender, id);
id++;
}
cursor = id;
claimed[msg.sender] = newTotal;
}
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-30 16:53:40
Comments
Log in to comment.
No comments yet.