// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
contract Access is AccessControl {
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function batchGrantRole(bytes32 role, address[] calldata accounts)
external
onlyRole(getRoleAdmin(role))
returns (bool[] memory)
{
uint256 length = accounts.length;
bool[] memory results = new bool[](length);
for (uint256 i = 0; i < length; ++i) {
results[i] = _grantRole(role, accounts[i]);
}
return results;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;
import {ERC721, ERC721Royalty} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Access} from "./Access.sol";
contract OGTCertification is ERC721Royalty, Ownable {
Access public access;
uint256 private _nextTokenId = 1;
uint256 public maxSupply;
bytes32 public stageRole;
uint256 public stageStart;
uint256 public stageFinish;
string private _baseUriValue;
mapping(address => bool) public hasMinted;
bytes32 public constant ANY = keccak256("ANY");
event StageSet(bytes32 indexed stageRole, uint256 stageStart, uint256 stageFinish);
event BaseURISet(string newBaseURI);
event AccessSet(address indexed newAccessAddress);
event TokenRoyaltySet(uint256 indexed tokenId, address indexed receiver, uint96 feeNumerator);
constructor(
address initialOwner,
address accessAddress,
uint256 _maxSupply,
string memory _uri,
address royaltyReceiver,
uint96 royaltyFeeNumerator
) ERC721("OGT", "OGT") Ownable(initialOwner) {
access = Access(accessAddress);
maxSupply = _maxSupply;
_baseUriValue = _uri;
_setDefaultRoyalty(royaltyReceiver, royaltyFeeNumerator);
}
function setStage(bytes32 _stageRole, uint256 _stageStart, uint256 _stageFinish) external {
require(access.hasRole(access.DEFAULT_ADMIN_ROLE(), _msgSender()), "Not allowed");
stageRole = _stageRole;
stageStart = _stageStart;
stageFinish = _stageFinish;
emit StageSet(stageRole, stageStart, stageFinish);
}
function safeMint() external {
address to = _msgSender();
require(!hasMinted[to], "Address has already minted a token");
require(_nextTokenId <= maxSupply, "Maximum supply reached");
require(block.timestamp >= stageStart, "Stage has not started yet");
require(block.timestamp <= stageFinish, "Stage has finished");
if (stageRole != ANY) {
require(stageRole != bytes32(0) && access.hasRole(stageRole, to), "Not allowed");
}
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
hasMinted[to] = true;
}
function _baseURI() internal view override returns (string memory) {
return _baseUriValue;
}
function getBaseURI() external view onlyOwner returns (string memory) {
return _baseURI();
}
function setBaseURI(string memory _newBaseURI) external onlyOwner {
_baseUriValue = _newBaseURI;
emit BaseURISet(_newBaseURI);
}
function setAccess(address _newAccessAddress) external onlyOwner {
require(_newAccessAddress != address(0), "New access control contract address cannot be the zero address.");
access = Access(_newAccessAddress);
emit AccessSet(_newAccessAddress);
}
function setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) external onlyOwner {
require(receiver != address(0), "Invalid receiver.");
require(feeNumerator > 0, "Fee must be > 0.");
_setTokenRoyalty(tokenId, receiver, feeNumerator);
emit TokenRoyaltySet(tokenId, receiver, feeNumerator);
}
}
{
"compilationTarget": {
"src/OGTCertification.sol": "OGTCertification"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=../node_modules/@openzeppelin/",
":forge-std/=../node_modules/forge-std/"
]
}
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"accessAddress","type":"address"},{"internalType":"uint256","name":"_maxSupply","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"internalType":"uint96","name":"royaltyFeeNumerator","type":"uint96"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"ERC2981InvalidDefaultRoyalty","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC2981InvalidDefaultRoyaltyReceiver","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"ERC2981InvalidTokenRoyalty","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC2981InvalidTokenRoyaltyReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAccessAddress","type":"address"}],"name":"AccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"newBaseURI","type":"string"}],"name":"BaseURISet","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"stageRole","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"stageStart","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stageFinish","type":"uint256"}],"name":"StageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint96","name":"feeNumerator","type":"uint96"}],"name":"TokenRoyaltySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"ANY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"access","outputs":[{"internalType":"contract Access","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAccessAddress","type":"address"}],"name":"setAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_stageRole","type":"bytes32"},{"internalType":"uint256","name":"_stageStart","type":"uint256"},{"internalType":"uint256","name":"_stageFinish","type":"uint256"}],"name":"setStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint96","name":"feeNumerator","type":"uint96"}],"name":"setTokenRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stageFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stageRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stageStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]