文件 1 的 5:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this;
return msg.data;
}
}
文件 2 的 5:IERC1155.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}
文件 3 的 5:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 4 的 5:NFTClaimManager.sol
pragma solidity 0.8.1;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
interface ICollection is IERC1155 {
function mint(
address account,
uint256 id,
uint256 amount,
bytes memory data,
string memory newURI
) external;
function setURI(string memory newURI) external;
function setTokenURI(uint256 id, string memory newURI) external;
}
contract NFTClaimManager is Ownable {
mapping(address => UserData) public userPoints;
mapping(uint256 => Nft) public nftPrices;
struct UserData {
uint256 points;
uint248 pointsSpent;
bool claimedBadge;
}
struct Nft {
uint248 price;
bool isBadge;
string uri;
}
ICollection public collection;
bool public claimsAllowed;
event NFTClaimed(address user, uint256 id, uint256 amount);
modifier allowed() {
require(claimsAllowed);
_;
}
constructor(ICollection _collection) {
collection = _collection;
}
function setUserPoints(address[] memory users, uint256[] memory points) external onlyOwner {
for (uint256 i = 0; i < users.length; i++) {
userPoints[users[i]] = UserData(points[i], 0, false);
}
}
function balanceOf(address user) external view returns (uint256) {
return userPoints[user].points - userPoints[user].pointsSpent;
}
function getPrice(uint256 id) external view returns (uint248) {
return nftPrices[id].price;
}
function setPrices(
uint256[] memory ids,
uint248[] memory prices,
string[] memory uris
) external onlyOwner {
for (uint256 i = 0; i < ids.length; i++) {
nftPrices[ids[i]] = Nft(prices[i], false, uris[i]);
}
}
function setBadges(uint256[] memory ids) external onlyOwner {
for (uint256 i = 0; i < ids.length; i++) {
nftPrices[ids[i]].isBadge = true;
}
}
function claim(uint256 id, uint256 amount) external allowed {
UserData storage user = userPoints[msg.sender];
Nft storage nft = nftPrices[id];
require(!nft.isBadge, "wrong function to claim badge");
uint256 price = uint256(nft.price);
require(price > 0, "nft doesn't exist");
uint256 pointsLeft = user.points - user.pointsSpent;
require(pointsLeft >= amount * price, "not enough points");
user.pointsSpent += uint248(price * amount);
collection.mint(msg.sender, id, amount, bytes(""), nft.uri);
emit NFTClaimed(msg.sender, id, amount);
}
function claimBadge(uint256 id) external allowed {
UserData storage user = userPoints[msg.sender];
Nft storage nft = nftPrices[id];
require(nft.isBadge, "not a badge");
require(!user.claimedBadge, "badge already claimed");
require(user.points > nft.price, "not enough points");
user.claimedBadge = true;
collection.mint(msg.sender, id, 1, bytes(""), nft.uri);
emit NFTClaimed(msg.sender, id, 1);
}
function setCollectionURI(string memory newURI) external onlyOwner {
collection.setURI(newURI);
}
function setTokenURI(uint256 id, string memory newURI) external onlyOwner {
collection.setTokenURI(id, newURI);
}
function toggleClaimsAllowed() external onlyOwner {
claimsAllowed = !claimsAllowed;
}
}
文件 5 的 5:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
{
"compilationTarget": {
"contracts/NFTClaimManager.sol": "NFTClaimManager"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ICollection","name":"_collection","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NFTClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"claimBadge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimsAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collection","outputs":[{"internalType":"contract ICollection","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"uint248","name":"","type":"uint248"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftPrices","outputs":[{"internalType":"uint248","name":"price","type":"uint248"},{"internalType":"bool","name":"isBadge","type":"bool"},{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"setBadges","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setCollectionURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint248[]","name":"prices","type":"uint248[]"},{"internalType":"string[]","name":"uris","type":"string[]"}],"name":"setPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"newURI","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256[]","name":"points","type":"uint256[]"}],"name":"setUserPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleClaimsAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPoints","outputs":[{"internalType":"uint256","name":"points","type":"uint256"},{"internalType":"uint248","name":"pointsSpent","type":"uint248"},{"internalType":"bool","name":"claimedBadge","type":"bool"}],"stateMutability":"view","type":"function"}]