From fc868ec13e64a4ea77746ca352dcaf58af513268 Mon Sep 17 00:00:00 2001 From: yawn Date: Sun, 17 Mar 2024 16:02:15 +0900 Subject: [PATCH 1/4] update: subsc nft --- hardhat/contracts/SubscriptionNFT.sol | 37 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/hardhat/contracts/SubscriptionNFT.sol b/hardhat/contracts/SubscriptionNFT.sol index e5ff4c3..260fed8 100644 --- a/hardhat/contracts/SubscriptionNFT.sol +++ b/hardhat/contracts/SubscriptionNFT.sol @@ -2,30 +2,51 @@ pragma solidity ^0.8.19; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; contract SubscriptionNFT is Initializable, - ERC721EnumerableUpgradeable, - OwnableUpgradeable + ERC721URIStorageUpgradeable, + AccessControlEnumerableUpgradeable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); uint256 private _tokenIds; + // 期限 + mapping(address => uint256) private subscriptionEnds; function initialize() public initializer { __ERC721_init("SubscriptionNFT", "SNFT"); __ERC721URIStorage_init(); - __Ownable_init(); + __AccessControlEnumerable_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + _grantRole(MINTER_ROLE, _msgSender()); + } + + function supportsInterface( + bytes4 interfaceId + ) + public + view + override( + ERC721Upgradeable, + AccessControlEnumerableUpgradeable + ) + returns (bool) + { + return super.supportsInterface(interfaceId); } - function mintNFT(address recipient, string memory tokenURI) - public onlyOwner + function mintNFT(address recipient, string memory tokenURI, uint256 subscriptionEnd) + public onlyRole(MINTER_ROLE) returns (uint256) { - _tokenIds++; + subscriptionEnds[recipient] = subscriptionEnd; + _tokenIds++; uint256 newItemId = _tokenIds; - _mint(recipient, newItemId); _setTokenURI(newItemId, tokenURI); + _mint(recipient, newItemId); return newItemId; } From 1aedb633224d2dfcb4d1d16aa49f0818878aa020 Mon Sep 17 00:00:00 2001 From: yawn Date: Sun, 17 Mar 2024 17:09:55 +0900 Subject: [PATCH 2/4] update: subscription ends --- hardhat/contracts/SubscriptionNFT.sol | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hardhat/contracts/SubscriptionNFT.sol b/hardhat/contracts/SubscriptionNFT.sol index 260fed8..313ad9c 100644 --- a/hardhat/contracts/SubscriptionNFT.sol +++ b/hardhat/contracts/SubscriptionNFT.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; +import {IMintPoint} from "./IMintPoint.sol"; contract SubscriptionNFT is Initializable, @@ -11,7 +12,7 @@ contract SubscriptionNFT is { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); uint256 private _tokenIds; - // 期限 + address public mintPointAddr; mapping(address => uint256) private subscriptionEnds; function initialize() public initializer { @@ -37,6 +38,10 @@ contract SubscriptionNFT is return super.supportsInterface(interfaceId); } + function setMintAddr(address _mintPointAddr) public onlyRole(DEFAULT_ADMIN_ROLE) { + mintPointAddr = _mintPointAddr; + } + function mintNFT(address recipient, string memory tokenURI, uint256 subscriptionEnd) public onlyRole(MINTER_ROLE) returns (uint256) @@ -51,4 +56,14 @@ contract SubscriptionNFT is return newItemId; } + function airdrop() public onlyRole(MINTER_ROLE) { + for (uint256 i = 1; i <= _tokenIds; i++) { + address owner = ownerOf(i); + if (subscriptionEnds[owner] > block.timestamp) { + IMintPoint(mintPointAddr).mintByMinter(owner, i, 1000); + } + // TODO: 一ヶ月に一回しかエアドロップできないような制限をつける + // 不平等にならないように気をつける必要あり(期限が5月15日か16日か、など日数がわずかに違うだけで、その月のエアドロをもらえるか否かが決まってしまうなど) + } + } } \ No newline at end of file From 6f7a783b5de64d2eb9c5f9c46cc96c64363b65a3 Mon Sep 17 00:00:00 2001 From: nfttakerun Date: Sun, 17 Mar 2024 17:22:04 +0900 Subject: [PATCH 3/4] new test file --- hardhat/test/SubscriptionNFT.ts | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 hardhat/test/SubscriptionNFT.ts diff --git a/hardhat/test/SubscriptionNFT.ts b/hardhat/test/SubscriptionNFT.ts new file mode 100644 index 0000000..d716d5a --- /dev/null +++ b/hardhat/test/SubscriptionNFT.ts @@ -0,0 +1,54 @@ +import { expect } from "chai"; +import { ethers, upgrades } from "hardhat"; +import { + SubscriptionNFT +} from "../typechain"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; + +const deploySubscriptionNFT = async () => { + const SubscriptionNFTFactory = await ethers.getContractFactory("SubscriptionNFT"); + const deployedSubscriptionNFT: SubscriptionNFT = (await upgrades.deployProxy( + SubscriptionNFTFactory, + [], + { + initializer: "initialize", + } + )) as any; + await deployedSubscriptionNFT.deployed(); + + console.log("SubscriptionNFT address:", deployedSubscriptionNFT.address); + + return deployedSubscriptionNFT; +}; + +// describe("SubscriptionNFT", function () { +// let subscriptionNFT: SubscriptionNFT; +// let owner: SignerWithAddress, addr1: SignerWithAddress; + +// before(async function () { +// [owner, addr1] = await ethers.getSigners(); +// subscriptionNFT = await deploySubscriptionNFT(); +// }); + +// describe("Contract deployment", function () { +// it("Should set the right owner", async function () { +// expect(await subscriptionNFT.owner()).to.equal(owner.address); +// }); + +// it("Should have the correct name and symbol", async function () { +// expect(await subscriptionNFT.name()).to.equal("SubscriptionNFT"); +// expect(await subscriptionNFT.symbol()).to.equal("SNFT"); +// }); +// }); + +// describe("Minting NFT", function () { +// it("Should mint an NFT to the specified address", async function () { +// const tokenURI = "https://example.com/nft"; +// await expect(subscriptionNFT.mintNFT(addr1.address, tokenURI)) +// .to.emit(subscriptionNFT, "Transfer") +// .withArgs(ethers.constants.AddressZero, addr1.address, 1); + +// expect(await subscriptionNFT.tokenURI(1)).to.equal(tokenURI); +// }); +// }); +// }); From dd692ad93131d71a70b4acc99b5d5d5056d473d6 Mon Sep 17 00:00:00 2001 From: yawn Date: Sun, 17 Mar 2024 17:56:14 +0900 Subject: [PATCH 4/4] wip: airdrop() --- hardhat/contracts/SubscriptionNFT.sol | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/hardhat/contracts/SubscriptionNFT.sol b/hardhat/contracts/SubscriptionNFT.sol index 313ad9c..c0bd56a 100644 --- a/hardhat/contracts/SubscriptionNFT.sol +++ b/hardhat/contracts/SubscriptionNFT.sol @@ -13,7 +13,12 @@ contract SubscriptionNFT is bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); uint256 private _tokenIds; address public mintPointAddr; - mapping(address => uint256) private subscriptionEnds; + mapping(uint256 => AirDropInfo) private airDropInfos; + + struct AirDropInfo { + uint256[] dates; + uint256 dropIndex; + } function initialize() public initializer { __ERC721_init("SubscriptionNFT", "SNFT"); @@ -42,14 +47,16 @@ contract SubscriptionNFT is mintPointAddr = _mintPointAddr; } - function mintNFT(address recipient, string memory tokenURI, uint256 subscriptionEnd) + function mintNFT(address recipient, string memory tokenURI, AirDropInfo calldata airDropInfo) public onlyRole(MINTER_ROLE) returns (uint256) { - subscriptionEnds[recipient] = subscriptionEnd; _tokenIds++; uint256 newItemId = _tokenIds; + + airDropInfos[newItemId] = airDropInfo; + _setTokenURI(newItemId, tokenURI); _mint(recipient, newItemId); @@ -58,12 +65,12 @@ contract SubscriptionNFT is function airdrop() public onlyRole(MINTER_ROLE) { for (uint256 i = 1; i <= _tokenIds; i++) { - address owner = ownerOf(i); - if (subscriptionEnds[owner] > block.timestamp) { - IMintPoint(mintPointAddr).mintByMinter(owner, i, 1000); + AirDropInfo memory airDropInfo = airDropInfos[i]; + if (airDropInfo.dates[airDropInfo.dropIndex] > block.timestamp) { + address owner = ownerOf(i); + IMintPoint(mintPointAddr).mintByMinter(owner, 1, 1000); } - // TODO: 一ヶ月に一回しかエアドロップできないような制限をつける - // 不平等にならないように気をつける必要あり(期限が5月15日か16日か、など日数がわずかに違うだけで、その月のエアドロをもらえるか否かが決まってしまうなど) + airDropInfos[i].dropIndex++; } } } \ No newline at end of file