KushlingsClaim

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": []
  }
}}

Tags:
Factory|addr:0xf8e567d269b9c9be82520a6c8cf76f364aa2735d|verified:true|block:23690924|tx:0x3efbf118a9684adac1097b4eb175b483303c111d4268d7a9d6f7cf19df00ad2a|first_check:1761839617

Submitted on: 2025-10-30 16:53:40

Comments

Log in to comment.

No comments yet.