|
| 1 | +// SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | + |
| 3 | +pragma solidity 0.8.14; |
| 4 | + |
| 5 | +import { AccessControl } from "openzeppelin-contracts/contracts/access/AccessControl.sol"; |
| 6 | +import { Pausable } from "openzeppelin-contracts/contracts/security/Pausable.sol"; |
| 7 | +import { ERC1155 } from "openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol"; |
| 8 | +import { ERC1155Supply } from "openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; |
| 9 | +import { Initializable } from "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"; |
| 10 | +import { UUPSUpgradeable } from "openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol"; |
| 11 | +import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol"; |
| 12 | + |
| 13 | +contract CyberNFT is |
| 14 | + ERC1155Supply, |
| 15 | + AccessControl, |
| 16 | + Pausable, |
| 17 | + UUPSUpgradeable, |
| 18 | + Initializable |
| 19 | +{ |
| 20 | + using Strings for uint256; |
| 21 | + /*////////////////////////////////////////////////////////////// |
| 22 | + STORAGE |
| 23 | + //////////////////////////////////////////////////////////////*/ |
| 24 | + |
| 25 | + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); |
| 26 | + address public recipient; |
| 27 | + |
| 28 | + struct MintPriceConfig { |
| 29 | + bool enable; |
| 30 | + uint256 price; |
| 31 | + } |
| 32 | + |
| 33 | + mapping(uint256 => MintPriceConfig) public mintPriceConfigs; |
| 34 | + |
| 35 | + /*////////////////////////////////////////////////////////////// |
| 36 | + CONSTRUCTOR & INITIALIZER |
| 37 | + //////////////////////////////////////////////////////////////*/ |
| 38 | + constructor() ERC1155("") {} |
| 39 | + |
| 40 | + function initialize(address _owner) external initializer { |
| 41 | + _grantRole(DEFAULT_ADMIN_ROLE, _owner); |
| 42 | + _grantRole(MANAGER_ROLE, _owner); |
| 43 | + } |
| 44 | + |
| 45 | + /*////////////////////////////////////////////////////////////// |
| 46 | + EXTERNAL |
| 47 | + //////////////////////////////////////////////////////////////*/ |
| 48 | + |
| 49 | + function pause() external onlyRole(MANAGER_ROLE) { |
| 50 | + _pause(); |
| 51 | + } |
| 52 | + |
| 53 | + function unpause() external onlyRole(MANAGER_ROLE) { |
| 54 | + _unpause(); |
| 55 | + } |
| 56 | + |
| 57 | + function setURI(string calldata newuri) external onlyRole(MANAGER_ROLE) { |
| 58 | + _setURI(newuri); |
| 59 | + } |
| 60 | + |
| 61 | + function setRecipient(address _recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { |
| 62 | + require(_recipient != address(0), "INVALID_RECIPIENT"); |
| 63 | + recipient = _recipient; |
| 64 | + } |
| 65 | + |
| 66 | + function setMintPriceConfig( |
| 67 | + uint256 tokenId, |
| 68 | + bool enable, |
| 69 | + uint256 price |
| 70 | + ) external onlyRole(DEFAULT_ADMIN_ROLE) { |
| 71 | + require(tokenId != 0, "INVALID_TOKEN_ID"); |
| 72 | + mintPriceConfigs[tokenId] = MintPriceConfig(enable, price); |
| 73 | + } |
| 74 | + |
| 75 | + function mint( |
| 76 | + address to, |
| 77 | + uint256 tokenId, |
| 78 | + uint256 amount |
| 79 | + ) external whenNotPaused onlyRole(MANAGER_ROLE) { |
| 80 | + require(tokenId != 0, "INVALID_TOKEN_ID"); |
| 81 | + require(amount != 0, "INVALID_AMOUNT"); |
| 82 | + |
| 83 | + _mint(to, tokenId, amount, ""); |
| 84 | + } |
| 85 | + |
| 86 | + function mintWithoutRole( |
| 87 | + address to, |
| 88 | + uint256 tokenId, |
| 89 | + uint256 amount |
| 90 | + ) external payable whenNotPaused { |
| 91 | + require(tokenId != 0, "INVALID_TOKEN_ID"); |
| 92 | + require(amount != 0, "INVALID_AMOUNT"); |
| 93 | + |
| 94 | + MintPriceConfig memory config = mintPriceConfigs[tokenId]; |
| 95 | + require(config.enable, "MINT_DISABLED"); |
| 96 | + |
| 97 | + _chargeAndRefundOverPayment(config.price*amount, msg.sender); |
| 98 | + |
| 99 | + _mint(to, tokenId, amount, ""); |
| 100 | + } |
| 101 | + |
| 102 | + /*////////////////////////////////////////////////////////////// |
| 103 | + EXTERNAL VIEW |
| 104 | + //////////////////////////////////////////////////////////////*/ |
| 105 | + function uri(uint256 tokenId) public view override returns (string memory) { |
| 106 | + return |
| 107 | + string( |
| 108 | + abi.encodePacked( |
| 109 | + ERC1155.uri(tokenId), |
| 110 | + tokenId.toString(), |
| 111 | + ".json" |
| 112 | + ) |
| 113 | + ); |
| 114 | + } |
| 115 | + |
| 116 | + function supportsInterface( |
| 117 | + bytes4 interfaceId |
| 118 | + ) public view override(ERC1155, AccessControl) returns (bool) { |
| 119 | + return |
| 120 | + ERC1155.supportsInterface(interfaceId) || |
| 121 | + AccessControl.supportsInterface(interfaceId); |
| 122 | + } |
| 123 | + |
| 124 | + function name() public pure returns (string memory) { |
| 125 | + return "Cyber NFT"; |
| 126 | + } |
| 127 | + |
| 128 | + function symbol() public pure returns (string memory) { |
| 129 | + return "CyberNFT"; |
| 130 | + } |
| 131 | + |
| 132 | + function getRecipient() public view returns (address) { |
| 133 | + return recipient; |
| 134 | + } |
| 135 | + |
| 136 | + function getMintPriceConfig(uint256 tokenId) public view returns (MintPriceConfig memory) { |
| 137 | + return mintPriceConfigs[tokenId]; |
| 138 | + } |
| 139 | + |
| 140 | + /*////////////////////////////////////////////////////////////// |
| 141 | + ONLY OWNER |
| 142 | + //////////////////////////////////////////////////////////////*/ |
| 143 | + |
| 144 | + function _authorizeUpgrade( |
| 145 | + address |
| 146 | + ) internal override onlyRole(DEFAULT_ADMIN_ROLE) {} |
| 147 | + |
| 148 | + /*////////////////////////////////////////////////////////////// |
| 149 | + PRIVATE |
| 150 | + //////////////////////////////////////////////////////////////*/ |
| 151 | + |
| 152 | + function _chargeAndRefundOverPayment( |
| 153 | + uint256 cost, |
| 154 | + address refundTo |
| 155 | + ) internal { |
| 156 | + require(msg.value >= cost, "INSUFFICIENT_FUNDS"); |
| 157 | + /** |
| 158 | + * Already checked msg.value >= cost |
| 159 | + */ |
| 160 | + uint256 overpayment; |
| 161 | + unchecked { |
| 162 | + overpayment = msg.value - cost; |
| 163 | + } |
| 164 | + |
| 165 | + if (overpayment > 0) { |
| 166 | + (bool refundSuccess, ) = refundTo.call{ value: overpayment }(""); |
| 167 | + require(refundSuccess, "REFUND_FAILED"); |
| 168 | + } |
| 169 | + if (cost > 0) { |
| 170 | + require(recipient != address(0), "INVALID_RECIPIENT"); |
| 171 | + (bool chargeSuccess, ) = recipient.call{ value: cost }(""); |
| 172 | + require(chargeSuccess, "CHARGE_FAILED"); |
| 173 | + } |
| 174 | + } |
| 175 | +} |
0 commit comments