From d0fddfdd732500f24ab710bad1b8eade661db6da Mon Sep 17 00:00:00 2001 From: 0xb337r007 <0xe4e5@proton.me> Date: Fri, 14 Jul 2023 15:05:30 +0200 Subject: [PATCH] add Base, Owner, and Master tokens --- contracts/mvp/BaseToken.sol | 155 ++++++++++++++++++++++++++++++++ contracts/mvp/CollectibleV1.sol | 138 +++------------------------- contracts/mvp/MasterToken.sol | 22 +++++ contracts/mvp/OwnerToken.sol | 31 +++++++ 4 files changed, 222 insertions(+), 124 deletions(-) create mode 100644 contracts/mvp/BaseToken.sol create mode 100644 contracts/mvp/MasterToken.sol create mode 100644 contracts/mvp/OwnerToken.sol diff --git a/contracts/mvp/BaseToken.sol b/contracts/mvp/BaseToken.sol new file mode 100644 index 0000000..97e3f27 --- /dev/null +++ b/contracts/mvp/BaseToken.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Mozilla Public License 2.0 +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; + +abstract contract BaseToken is + Context, + ERC721Enumerable +{ + using Counters for Counters.Counter; + + // State variables + + Counters.Counter private _tokenIdTracker; + + /** + * If we want unlimited total supply we should set maxSupply to 2^256-1. + */ + uint256 public maxSupply; + + address public ownerToken; + address public masterToken; + + /** + * If set to true, the contract owner can burn any token. + */ + bool public remoteBurnable; + + /** + * If set to false it acts as a soulbound token. + */ + bool public transferable; + + string public baseTokenURI; + + constructor( + string memory _name, + string memory _symbol, + uint256 _maxSupply, + bool _remoteBurnable, + bool _transferable, + string memory _baseTokenURI, + address _ownerToken, + address _masterToken + ) ERC721(_name, _symbol) { + maxSupply = _maxSupply; + remoteBurnable = _remoteBurnable; + transferable = _transferable; + baseTokenURI = _baseTokenURI; + ownerToken = _ownerToken; + masterToken = _masterToken; + + require(ownerToken != address(0x0) || masterToken != address(0x0), "owner or master tokens required"); + } + + modifier onlyOwner() { + require( + (ownerToken == address(0) || IERC721(ownerToken).balanceOf(msg.sender) > 0) || + (masterToken == address(0) || IERC721(masterToken).balanceOf(msg.sender) > 0), + "Not authorized" + ); + + _; + } + + // Events + + // External functions + + function setMaxSupply(uint256 newMaxSupply) virtual external onlyOwner { + require(newMaxSupply >= totalSupply(), "MAX_SUPPLY_LOWER_THAN_TOTAL_SUPPLY"); + maxSupply = newMaxSupply; + } + + /** + * @dev Creates a new token for each address in `addresses`. Its token ID will be automatically + * assigned (and available on the emitted {IERC721-Transfer} event), and the token + * URI autogenerated based on the base URI passed at construction. + * + */ + function mintTo(address[] memory addresses) external onlyOwner { + // We cannot just use totalSupply() to create the new tokenId because tokens + // can be burned so we use a separate counter. + require(_tokenIdTracker.current() + addresses.length <= maxSupply, "MAX_SUPPLY_REACHED"); + + for (uint256 i = 0; i < addresses.length; i++) { + _safeMint(addresses[i], _tokenIdTracker.current(), ""); + _tokenIdTracker.increment(); + } + } + + // Public functions + + function mintedCount() public view returns (uint256) { + return _tokenIdTracker.current(); + } + + /** + * @notice remoteBurn allows the owner to burn a token + * @param tokenIds The list of token IDs to be burned + */ + function remoteBurn(uint256[] memory tokenIds) public onlyOwner { + require(remoteBurnable, "NOT_REMOTE_BURNABLE"); + + for (uint256 i = 0; i < tokenIds.length; i++) { + _burn(tokenIds[i]); + } + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + // Internal functions + + /** + * @notice + * @dev + */ + function _baseURI() internal view virtual override returns (string memory) { + return baseTokenURI; + } + + /** + * @notice + * @dev + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721Enumerable) { + if (from != address(0) && to != address(0) && !transferable) { + revert("not transferable"); + } + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + // Private functions +} diff --git a/contracts/mvp/CollectibleV1.sol b/contracts/mvp/CollectibleV1.sol index 7db8238..d18f815 100644 --- a/contracts/mvp/CollectibleV1.sol +++ b/contracts/mvp/CollectibleV1.sol @@ -1,136 +1,26 @@ // SPDX-License-Identifier: Mozilla Public License 2.0 pragma solidity ^0.8.17; -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; -import "@openzeppelin/contracts/utils/Context.sol"; -import "@openzeppelin/contracts/utils/Counters.sol"; - -contract CollectibleV1 is - Context, - ERC721Enumerable, - Ownable -{ - using Counters for Counters.Counter; - - // State variables - - Counters.Counter private _tokenIdTracker; - - /** - * If we want unlimited total supply we should set maxSupply to 2^256-1. - */ - uint256 public maxSupply; - - /** - * If set to true, the contract owner can burn any token. - */ - bool public remoteBurnable; - - /** - * If set to false it acts as a soulbound token. - */ - bool public transferable; - - string public baseTokenURI; +import "./BaseToken.sol"; +contract CollectibleV1 is BaseToken { constructor( string memory _name, string memory _symbol, uint256 _maxSupply, bool _remoteBurnable, bool _transferable, - string memory _baseTokenURI - ) ERC721(_name, _symbol) { - maxSupply = _maxSupply; - remoteBurnable = _remoteBurnable; - transferable = _transferable; - baseTokenURI = _baseTokenURI; + string memory _baseTokenURI, + address _ownerToken, + address _masterToken + ) BaseToken( + _name, + _symbol, + _maxSupply, + _remoteBurnable, + _transferable, + _baseTokenURI, + _ownerToken, + _masterToken) { } - - // Events - - // External functions - - function setMaxSupply(uint256 newMaxSupply) external onlyOwner { - require(newMaxSupply >= totalSupply(), "MAX_SUPPLY_LOWER_THAN_TOTAL_SUPPLY"); - maxSupply = newMaxSupply; - } - - /** - * @dev Creates a new token for each address in `addresses`. Its token ID will be automatically - * assigned (and available on the emitted {IERC721-Transfer} event), and the token - * URI autogenerated based on the base URI passed at construction. - * - */ - function mintTo(address[] memory addresses) external onlyOwner { - // We cannot just use totalSupply() to create the new tokenId because tokens - // can be burned so we use a separate counter. - require(_tokenIdTracker.current() + addresses.length <= maxSupply, "MAX_SUPPLY_REACHED"); - - for (uint256 i = 0; i < addresses.length; i++) { - _safeMint(addresses[i], _tokenIdTracker.current(), ""); - _tokenIdTracker.increment(); - } - } - - // Public functions - - function mintedCount() public view returns (uint256) { - return _tokenIdTracker.current(); - } - - /** - * @notice remoteBurn allows the owner to burn a token - * @param tokenIds The list of token IDs to be burned - */ - function remoteBurn(uint256[] memory tokenIds) public onlyOwner { - require(remoteBurnable, "NOT_REMOTE_BURNABLE"); - - for (uint256 i = 0; i < tokenIds.length; i++) { - _burn(tokenIds[i]); - } - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721Enumerable) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - // Internal functions - - /** - * @notice - * @dev - */ - function _baseURI() internal view virtual override returns (string memory) { - return baseTokenURI; - } - - /** - * @notice - * @dev - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 firstTokenId, - uint256 batchSize - ) internal virtual override(ERC721Enumerable) { - if (from != address(0) && to != address(0) && !transferable) { - revert("not transferable"); - } - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); - } - - // Private functions } diff --git a/contracts/mvp/MasterToken.sol b/contracts/mvp/MasterToken.sol new file mode 100644 index 0000000..30a9326 --- /dev/null +++ b/contracts/mvp/MasterToken.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Mozilla Public License 2.0 +pragma solidity ^0.8.17; + +import "./BaseToken.sol"; + +contract MasterToken is BaseToken { + constructor( + string memory _name, + string memory _symbol, + string memory _baseTokenURI, + address _ownerToken + ) BaseToken( + _name, + _symbol, + type(uint256).max, + true, + false, + _baseTokenURI, + _ownerToken, + address(0x0)) { + } +} diff --git a/contracts/mvp/OwnerToken.sol b/contracts/mvp/OwnerToken.sol new file mode 100644 index 0000000..98399e6 --- /dev/null +++ b/contracts/mvp/OwnerToken.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Mozilla Public License 2.0 +pragma solidity ^0.8.17; + +import "./BaseToken.sol"; +import "./MasterToken.sol"; + +contract OwnerToken is BaseToken { + constructor( + string memory _name, + string memory _symbol, + string memory _baseTokenURI, + string memory _masterName, + string memory _masterSymbol, + string memory _masterBaseTokenURI + ) BaseToken( + _name, + _symbol, + 1, + false, + true, + _baseTokenURI, + address(this), + address(this)) + { + new MasterToken(_masterName, _masterSymbol, _masterBaseTokenURI, address(this)); + } + + function setMaxSupply(uint256 _newMaxSupply) override external onlyOwner { + revert("max supply locked"); + } +}