Hey
I wanted to make a NFT smart contract that would let me make sub collections inside the main contract. This is mostly because I sense that it is quite expensive to put any contract on main, so I thought this way I could reuse the same contract for a bunch of projects.
I should mention that im prepending a collection index into the tokenId. For instance 1100002 would be collection 11 token 2.. hope that makes sense.
I'm new to smart contract development so I might be doing a lot of stuff wrong. Any feedback would be much appreciated.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract KomputerToken is ERC721, ERC721URIStorage, Ownable
{
using Strings for uint256;
struct Collection {
bytes ipfs;
bytes uri;
address collectionOwner;
uint256 mintPriceInWei;
uint256 numberOfTokens;
uint256 currentTokenIndex;
bool saleIsActive;
}
Collection[] public collections;
string baseURI = "https://gateway.pinata.cloud/ipfs/";
constructor() ERC721("Komputerwolf", "KW2000") {
//collections.push(Collection("", "", 0x0000000000000000000000000000000000000000, 0, 0, 0, false));
}
function createCollection(bytes memory ipfs, bytes memory uri, uint256 numberOfTokens, address collectionOwner, uint256 mintPriceInWei)
external onlyOwner {
collections.push(Collection(ipfs, uri, collectionOwner, mintPriceInWei, numberOfTokens, 1, false));
}
function editCollection(uint256 collectionIndex, bytes memory ipfs, bytes memory uri, uint256 numberOfTokens, address collectionOwner, uint256 mintPriceInWei)
external onlyOwner {
collections[collectionIndex] = Collection(ipfs, uri, collectionOwner, mintPriceInWei, numberOfTokens, 1, false);
}
function setCollectionActive(uint256 collectionIndex, bool state) external onlyOwner {
collections[collectionIndex].saleIsActive = state;
}
function mintNFT(address recipient, uint256 collectionIndex) public payable {
require(collections[collectionIndex].saleIsActive, "Sale must be active to mint");
require(collections[collectionIndex].currentTokenIndex <= collections[collectionIndex].numberOfTokens, "Max token count has been reached");
require(collections[collectionIndex].mintPriceInWei <= msg.value, "Ether value sent is not correct");
address payable collectionOwner = payable(collections[collectionIndex].collectionOwner);
collectionOwner.transfer(msg.value);
uint256 newTokenId = generateTokenId(collectionIndex);
_safeMint(recipient, newTokenId);
collections[collectionIndex].currentTokenIndex += 1;
}
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
uint256 collectionIndex = getCollectionIndexFromTokenId(tokenId);
bytes memory collectionHash = abi.encodePacked(collections[collectionIndex].ipfs);
uint256 tokenIndex = getTokenIndexFromTokenId(tokenId);
bytes memory fullUrl = abi.encodePacked(baseURI, collectionHash, "/", tokenIndex.toString());
return string(fullUrl);
}
function generateTokenId(uint256 collectionIndex) internal view returns (uint256){
// format: collection index -- index in collection
// #0 00000
// (100000*1001)+1
uint256 index = (10000 * collectionIndex) + collections[collectionIndex].currentTokenIndex;
return index;
}
function getCollectionIndexFromTokenId(uint256 tokenId) internal pure returns (uint256)
{
uint a = tokenId / 10000;
// first digits after last 4
return a;
}
function getTokenIndexFromTokenId(uint256 tokenId) internal pure returns (uint256)
{
uint a = tokenId % 10000;
// last 4 digits
return a;
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function getCollectionSaleActive(uint256 collectionIndex) public view returns (bool) {
return collections[collectionIndex].saleIsActive;
}
function getCurrentTokenIndex(uint256 collectionIndex) public view returns (uint256) {
return collections[collectionIndex].currentTokenIndex;
}
function getMaxTokenCount(uint256 collectionIndex) public view returns (uint256) {
return collections[collectionIndex].numberOfTokens;
}
}