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": {
    "KushlingsClaim.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC721 {
    function ownerOf(uint256 tokenId) external view returns (address);
    function transferFrom(address from, address to, uint256 tokenId) external;
}

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;
    }
}

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;
    }
}

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, "ReentrancyGuard: reentrant call");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

contract KushlingsClaim is Ownable, ReentrancyGuard {
    IERC721 public nft;
    address public treasury;
    uint256 public startId;
    uint256 public endId;
    uint256 public cursor;
    bytes32 public merkleRoot;
    bool public paused;

    mapping(address => uint256) public claimed;

    event Claimed(address indexed to, uint256 indexed tokenId);
    event MerkleRootSet(bytes32 newRoot);

    error BadRange();
    error PausedErr();
    error SoldOut();
    error NotAllowlisted();
    error ExceedsAllowance(uint256 want, uint256 max);

    constructor(
        address _nft,
        address _treasury,
        uint256 _startId,
        uint256 _endId,
        bytes32 _merkleRoot,
        address _owner
    ) Ownable(_owner) {
        require(_startId <= _endId, "bad range");
        nft = IERC721(_nft);
        treasury = _treasury;
        startId = _startId;
        endId = _endId;
        cursor = _startId;
        merkleRoot = _merkleRoot;
        paused = true;
    }

    function setPaused(bool _paused) external onlyOwner {
        paused = _paused;
    }

    function setRange(uint256 _startId, uint256 _endId) external onlyOwner {
        if (_startId > _endId) revert BadRange();
        startId = _startId;
        endId = _endId;
        if (cursor < _startId) cursor = _startId;
    }

    function setMerkleRoot(bytes32 _root) external onlyOwner {
        merkleRoot = _root;
        emit MerkleRootSet(_root);
    }

    function maxFor(address account, uint256 maxFromLeaf, bytes32[] calldata proof)
        public
        view
        returns (uint256)
    {
        if (merkleRoot == bytes32(0)) {
            return type(uint256).max;
        }
        bytes32 leaf = keccak256(abi.encodePacked(account, maxFromLeaf));
        if (!MerkleProof.verify(proof, merkleRoot, leaf)) revert NotAllowlisted();
        return maxFromLeaf;
    }

    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++) {
                while (id <= endId && nft.ownerOf(id) != treasury) {
                    id++;
                }
                if (id > endId) revert SoldOut();
                
                nft.transferFrom(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:0xc984daa1f67da6a0deabaa39f532b7716247f831|verified:true|block:23698173|tx:0xf2f1f694e9f844e3272d5043fc96f5d81db3cd9cf0d3f1947af8f88602dbdc77|first_check:1761927395

Submitted on: 2025-10-31 17:16:37

Comments

Log in to comment.

No comments yet.