136 lines
4.2 KiB
Solidity
136 lines
4.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.19;
|
|
|
|
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import {HasSignature} from "../core/HasSignature.sol";
|
|
|
|
/**
|
|
* Contract for the activity of NFT claim stage 2.
|
|
*/
|
|
interface IClaimAbleNFT {
|
|
function safeMint(address to, uint256 tokenID) external;
|
|
}
|
|
|
|
contract NFTClaimStage2 is HasSignature, ReentrancyGuard {
|
|
using SafeERC20 for IERC20;
|
|
struct MintConfig {
|
|
uint256 parse1MaxSupply; // max supply for phase1
|
|
uint256 maxSupply; // max supply for phase2
|
|
address currency; // token address which user must pay to mint
|
|
uint256 mintPrice; // in wei
|
|
address feeToAddress; // wallet address to receive mint fee
|
|
}
|
|
// parse: 0: not open or end, 1: phase1, 2: phase2
|
|
uint256 public mintParse = 0;
|
|
|
|
uint256 public immutable _CACHED_CHAIN_ID;
|
|
address public immutable _CACHED_THIS;
|
|
address public immutable nftAddress;
|
|
|
|
address public verifier;
|
|
MintConfig public mintConfig;
|
|
uint256 public parse1Count;
|
|
uint256 public totalCount;
|
|
|
|
event NFTClaimed(address indexed nftAddress, address indexed to, uint256[] ids);
|
|
|
|
event ParseUpdated(uint256 _parse);
|
|
event MintConfigUpdated(MintConfig config);
|
|
event VerifierUpdated(address indexed verifier);
|
|
|
|
constructor(address _nftAddress, address _verifier, MintConfig memory _mintConfig) {
|
|
_CACHED_CHAIN_ID = block.chainid;
|
|
_CACHED_THIS = address(this);
|
|
nftAddress = _nftAddress;
|
|
verifier = _verifier;
|
|
mintConfig = _mintConfig;
|
|
}
|
|
|
|
modifier whenNotPaused() {
|
|
require(mintParse > 0, "NFTClaimer: not begin or ended");
|
|
_;
|
|
}
|
|
|
|
function updateMintParse(uint256 _mintParse) external onlyOwner {
|
|
mintParse = _mintParse;
|
|
emit ParseUpdated(_mintParse);
|
|
}
|
|
|
|
function updateMintConfig(MintConfig calldata config) external onlyOwner {
|
|
mintConfig = config;
|
|
emit MintConfigUpdated(config);
|
|
}
|
|
|
|
/**
|
|
* @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[] memory ids,
|
|
uint256 tokenAmount,
|
|
uint256 saltNonce,
|
|
bytes calldata signature
|
|
) external nonReentrant whenNotPaused {
|
|
// get current parse;
|
|
uint256 count = ids.length;
|
|
require(count > 0, "NFTClaimer: ids length must be greater than 0");
|
|
if (mintParse == 1) {
|
|
require(count <= mintConfig.parse1MaxSupply - parse1Count, "NFTClaimer: exceed parse 1 max supply");
|
|
} else {
|
|
require(count <= mintConfig.maxSupply - totalCount, "NFTClaimer: exceed max supply");
|
|
}
|
|
require(tokenAmount >= mintConfig.mintPrice * count, "NFTClaimer: insufficient token amount");
|
|
address to = _msgSender();
|
|
bytes32 criteriaMessageHash = getMessageHash(
|
|
to,
|
|
nftAddress,
|
|
ids,
|
|
tokenAmount,
|
|
_CACHED_THIS,
|
|
_CACHED_CHAIN_ID,
|
|
saltNonce
|
|
);
|
|
checkSigner(verifier, criteriaMessageHash, signature);
|
|
IERC20(mintConfig.currency).safeTransferFrom(to, mintConfig.feeToAddress, tokenAmount);
|
|
for (uint256 i = 0; i < count; ++i) {
|
|
IClaimAbleNFT(nftAddress).safeMint(to, ids[i]);
|
|
}
|
|
// require(count > 2, "run to here");
|
|
totalCount += count;
|
|
if (mintParse == 1) {
|
|
parse1Count += count;
|
|
}
|
|
_useSignature(signature);
|
|
emit NFTClaimed(nftAddress, to, ids);
|
|
}
|
|
|
|
function getMessageHash(
|
|
address _to,
|
|
address _address,
|
|
uint256[] memory _ids,
|
|
uint256 _tokenAmount,
|
|
address _contract,
|
|
uint256 _chainId,
|
|
uint256 _saltNonce
|
|
) public pure returns (bytes32) {
|
|
bytes memory encoded = abi.encodePacked(_to, _address, _tokenAmount, _contract, _chainId, _saltNonce);
|
|
for (uint256 i = 0; i < _ids.length; ++i) {
|
|
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
|
|
}
|
|
return keccak256(encoded);
|
|
}
|
|
}
|