|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +// Compatible with OpenZeppelin Contracts ^5.0.0 |
| 3 | +pragma solidity ^0.8.20; |
| 4 | + |
| 5 | +import {ERC721NonTransferrableUpgradable} from "../NFT/ERC721NonTransferrableUpgradable.sol"; |
| 6 | +import {GovernorRootstockCollective} from "../GovernorRootstockCollective.sol"; |
| 7 | + |
| 8 | +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; |
| 9 | +import "hardhat/console.sol"; |
| 10 | + |
| 11 | +contract VanguardNFTRootstockCollective is ERC721NonTransferrableUpgradable { |
| 12 | + using Strings for uint256; |
| 13 | + |
| 14 | + event IpfsFolderChanged(uint256 newNumFiles, string newIpfs); |
| 15 | + |
| 16 | + error VanguardCannotMint(); |
| 17 | + error MintError(string reason); |
| 18 | + |
| 19 | + GovernorRootstockCollective public governor; |
| 20 | + // Counter for the total number of minted tokens |
| 21 | + uint256 private _totalMinted; |
| 22 | + // number of metadata files in the IPFS directory |
| 23 | + uint256 private _maxSupply; |
| 24 | + // IPFS CID of the tokens metadata directory |
| 25 | + string private _folderIpfsCid; |
| 26 | + |
| 27 | + /// @custom:oz-upgrades-unsafe-allow constructor |
| 28 | + constructor() { |
| 29 | + _disableInitializers(); |
| 30 | + } |
| 31 | + |
| 32 | + function initialize( |
| 33 | + address initialOwner, |
| 34 | + GovernorRootstockCollective governorAddress, |
| 35 | + uint256 maxSupply, |
| 36 | + string calldata ipfsFolderCid |
| 37 | + ) public initializer { |
| 38 | + require(address(governorAddress) != address(0), "VanguardNFTRootstockCollective: No governor address"); |
| 39 | + __ERC721UpgradableBase_init("VanguardNFTRootstockCollective", "VanNFT", initialOwner); |
| 40 | + governor = governorAddress; |
| 41 | + setIpfsFolder(maxSupply, ipfsFolderCid); |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * @dev Sets a new IPFS folder and updates the maximum supply of tokens that can be minted. |
| 46 | + * This function is meant to be called by an admin when the metadata folder on IPFS is updated. |
| 47 | + * It ensures that the new maximum supply is greater than the previous one. |
| 48 | + * @param newMaxSupply The new maximum number of tokens that can be minted. |
| 49 | + * @param newIpfsCid The new IPFS CID for the metadata folder. |
| 50 | + */ |
| 51 | + function setIpfsFolder(uint256 newMaxSupply, string calldata newIpfsCid) public virtual onlyOwner { |
| 52 | + require(newMaxSupply >= _maxSupply, "VanguardNFTRootstockCollective: Invalid max supply"); |
| 53 | + _maxSupply = newMaxSupply; |
| 54 | + _folderIpfsCid = newIpfsCid; |
| 55 | + emit IpfsFolderChanged(newMaxSupply, newIpfsCid); |
| 56 | + } |
| 57 | + |
| 58 | + function mint() external virtual { |
| 59 | + address caller = _msgSender(); |
| 60 | + |
| 61 | + try governor.proposalCount() returns (uint count) { |
| 62 | + uint8 counter = 10; |
| 63 | + bool hasEverVoted = false; |
| 64 | + |
| 65 | + while (counter != 0) { |
| 66 | + (uint256 proposalId, , , , ) = governor.proposalDetailsAt(count - counter); |
| 67 | + |
| 68 | + bool hasVoted = governor.hasVoted(proposalId, caller); |
| 69 | + |
| 70 | + if (hasVoted) { |
| 71 | + hasEverVoted = hasVoted; |
| 72 | + break; |
| 73 | + } |
| 74 | + |
| 75 | + counter--; |
| 76 | + } |
| 77 | + |
| 78 | + if (!hasEverVoted) { |
| 79 | + revert VanguardCannotMint(); |
| 80 | + } |
| 81 | + |
| 82 | + //here we mint |
| 83 | + uint256 tokenId = ++_totalMinted; |
| 84 | + string memory fileName = string.concat(tokenId.toString(), ".json"); // 1.json, 2.json ... |
| 85 | + _safeMint(caller, tokenId); |
| 86 | + _setTokenURI(tokenId, fileName); |
| 87 | + } catch Error(string memory reason) { |
| 88 | + revert MintError(reason); |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * @dev Returns the number of tokens available for minting |
| 94 | + */ |
| 95 | + function tokensAvailable() public view virtual returns (uint256) { |
| 96 | + if (_totalMinted >= _maxSupply) return 0; |
| 97 | + return _maxSupply - _totalMinted; |
| 98 | + } |
| 99 | + |
| 100 | + /** |
| 101 | + * @dev Returns the token ID for a given owner address. |
| 102 | + * This is a simplified version of the `tokenOfOwnerByIndex` function without the index |
| 103 | + * parameter, since a community member can only own one token. |
| 104 | + */ |
| 105 | + function tokenIdByOwner(address owner) public view virtual returns (uint256) { |
| 106 | + return tokenOfOwnerByIndex(owner, 0); |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * @dev Returns the token IPFS URI for the given owner address. |
| 111 | + * This utility function combines two view functions. |
| 112 | + */ |
| 113 | + function tokenUriByOwner(address owner) public view virtual returns (string memory) { |
| 114 | + return tokenURI(tokenIdByOwner(owner)); |
| 115 | + } |
| 116 | + |
| 117 | + function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {} |
| 118 | +} |
0 commit comments