// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {HasSignature} from "../core/HasSignature.sol"; /** * Contract for the activity of NFT claim stage 2. */ interface IClaimAbleNFT { function safeMint( address to ) external returns (uint256); } contract NFTClaimStage2 is HasSignature, ReentrancyGuard{ uint256 private immutable _CACHED_CHAIN_ID; address private immutable _CACHED_THIS; address public immutable nftAddress; address public verifier; bool public isPaused = false; mapping(address user => uint256 status) public claimHistory; event NFTClaimed( address indexed nftAddress, address indexed to, uint256 tokenId, uint256 nonce ); event StateUpdated(bool isPaused); event VerifierUpdated(address indexed verifier); constructor(address _nftAddress) { _CACHED_CHAIN_ID = block.chainid; _CACHED_THIS = address(this); nftAddress = _nftAddress; } modifier whenNotPaused() { require(!isPaused, "NFTClaimer: paused"); _; } function updatePaused(bool _isPaused) external onlyOwner { isPaused = _isPaused; emit StateUpdated(_isPaused); } /** * @dev update verifier address */ function updateVerifier(address _verifier) external onlyOwner { require(_verifier != address(0), "NFTClaimer: address can not be zero"); verifier = _verifier; emit VerifierUpdated(_verifier); } /** * @dev claim NFT * Get whitelist signature from a third-party service, then call this method to claim NFT * @param saltNonce nonce * @param signature signature */ function claim( uint256 saltNonce, bytes calldata signature ) external nonReentrant whenNotPaused { address to = _msgSender(); require(claimHistory[to] == 0, "NFTClaimer: already claimed"); bytes32 criteriaMessageHash = getMessageHash( to, nftAddress, saltNonce ); checkSigner(verifier, criteriaMessageHash, signature); uint256 tokenId = IClaimAbleNFT(nftAddress).safeMint(to); claimHistory[to] = tokenId; _useSignature(signature); emit NFTClaimed(nftAddress, to, tokenId, saltNonce); } function getMessageHash( address _to, address _address, uint256 _saltNonce ) public view returns (bytes32) { bytes memory encoded = abi.encodePacked( _to, _address, _CACHED_CHAIN_ID, _CACHED_THIS, _saltNonce ); return keccak256(encoded); } }