Skip to content

Commit

Permalink
add master/owner tokens to erc20 token
Browse files Browse the repository at this point in the history
  • Loading branch information
0xb337r007 committed Oct 6, 2023
1 parent ff7de4a commit 5763d7d
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 72 deletions.
33 changes: 33 additions & 0 deletions contracts/CommunityOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Mozilla Public License 2.0

pragma solidity ^0.8.17;

import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract CommunityOwnable {
error CommunityOwnable_InvalidTokenAddress();
error CommunityOwnable_NotAuthorized();

address public immutable ownerToken;
address public immutable masterToken;

constructor(address _ownerToken, address _masterToken) {
ownerToken = _ownerToken;
masterToken = _masterToken;

if (ownerToken == address(0) && masterToken == address(0)) {
revert CommunityOwnable_InvalidTokenAddress();
}
}

/// @dev Reverts if the msg.sender does not possess either an OwnerToken or a MasterToken.
modifier onlyCommunityOwnerOrTokenMaster() {
if (
(ownerToken != address(0) && IERC721(ownerToken).balanceOf(msg.sender) == 0)
&& (masterToken != address(0) && IERC721(masterToken).balanceOf(msg.sender) == 0)
) {
revert CommunityOwnable_NotAuthorized();
}
_;
}
}
63 changes: 22 additions & 41 deletions contracts/tokens/BaseToken.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// SPDX-License-Identifier: Mozilla Public License 2.0
pragma solidity ^0.8.17;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { Context } from "@openzeppelin/contracts/utils/Context.sol";
import { Counters } from "@openzeppelin/contracts/utils/Counters.sol";

abstract contract BaseToken is Context, ERC721Enumerable {
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
import {CommunityOwnable} from "../CommunityOwnable.sol";

abstract contract BaseToken is Context, ERC721Enumerable, CommunityOwnable {
using Counters for Counters.Counter;

error BaseToken_InvalidTokenAddress();
error BaseToken_NotAuthorized();
error BaseToken_MaxSupplyLowerThanTotalSupply();
error BaseToken_MaxSupplyReached();
error BaseToken_NotRemoteBurnable();
Expand All @@ -25,10 +24,6 @@ abstract contract BaseToken is Context, ERC721Enumerable {
* If we want unlimited total supply we should set maxSupply to 2^256-1.
*/
uint256 public maxSupply;

address public immutable ownerToken;
address public immutable masterToken;

/**
* If set to true, the contract owner can burn any token.
*/
Expand All @@ -50,36 +45,20 @@ abstract contract BaseToken is Context, ERC721Enumerable {
string memory _baseTokenURI,
address _ownerToken,
address _masterToken
)
ERC721(_name, _symbol)
{
) ERC721(_name, _symbol) CommunityOwnable(_ownerToken, _masterToken) {
maxSupply = _maxSupply;
remoteBurnable = _remoteBurnable;
transferable = _transferable;
baseTokenURI = _baseTokenURI;
ownerToken = _ownerToken;
masterToken = _masterToken;

if (ownerToken == address(0) && masterToken == address(0)) {
revert BaseToken_InvalidTokenAddress();
}
}

modifier onlyOwner() {
if (
(ownerToken != address(0) && IERC721(ownerToken).balanceOf(msg.sender) == 0)
&& (masterToken != address(0) && IERC721(masterToken).balanceOf(msg.sender) == 0)
) {
revert BaseToken_NotAuthorized();
}
_;
}

// Events

// External functions

function setMaxSupply(uint256 newMaxSupply) external virtual onlyOwner {
function setMaxSupply(
uint256 newMaxSupply
) external virtual onlyCommunityOwnerOrTokenMaster {
if (newMaxSupply < totalSupply()) {
revert BaseToken_MaxSupplyLowerThanTotalSupply();
}
Expand All @@ -92,7 +71,9 @@ abstract contract BaseToken is Context, ERC721Enumerable {
* URI autogenerated based on the base URI passed at construction.
*
*/
function mintTo(address[] memory addresses) public onlyOwner {
function mintTo(
address[] memory addresses
) public onlyCommunityOwnerOrTokenMaster {
if (_tokenIdTracker.current() + addresses.length > maxSupply) {
revert BaseToken_MaxSupplyReached();
}
Expand All @@ -109,7 +90,9 @@ abstract contract BaseToken is Context, ERC721Enumerable {
* @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 {
function remoteBurn(
uint256[] memory tokenIds
) public onlyCommunityOwnerOrTokenMaster {
if (!remoteBurnable) revert BaseToken_NotRemoteBurnable();

for (uint256 i = 0; i < tokenIds.length; i++) {
Expand All @@ -120,7 +103,9 @@ abstract contract BaseToken is Context, ERC721Enumerable {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Enumerable) returns (bool) {
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC721Enumerable) returns (bool) {
return super.supportsInterface(interfaceId);
}

Expand Down Expand Up @@ -152,11 +137,7 @@ abstract contract BaseToken is Context, ERC721Enumerable {
address to,
uint256 firstTokenId,
uint256 batchSize
)
internal
virtual
override(ERC721Enumerable)
{
) internal virtual override(ERC721Enumerable) {
if (from != address(0) && to != address(0) && !transferable) {
revert BaseToken_NotTransferable();
}
Expand Down
26 changes: 16 additions & 10 deletions contracts/tokens/CommunityERC20.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: Mozilla Public License 2.0
pragma solidity ^0.8.17;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Context } from "@openzeppelin/contracts/utils/Context.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {CommunityOwnable} from "../CommunityOwnable.sol";

contract CommunityERC20 is Context, Ownable, ERC20 {
contract CommunityERC20 is Context, Ownable, ERC20, CommunityOwnable {
error CommunityERC20_MaxSupplyLowerThanTotalSupply();
error CommunityERC20_MaxSupplyReached();
error CommunityERC20_MismatchingAddressesAndAmountsLengths();
Expand All @@ -21,10 +22,10 @@ contract CommunityERC20 is Context, Ownable, ERC20 {
string memory _name,
string memory _symbol,
uint8 _decimals,
uint256 _maxSupply
)
ERC20(_name, _symbol)
{
uint256 _maxSupply,
address _ownerToken,
address _masterToken
) ERC20(_name, _symbol) CommunityOwnable(_ownerToken, _masterToken) {
maxSupply = _maxSupply;
customDecimals = _decimals;
}
Expand All @@ -33,7 +34,9 @@ contract CommunityERC20 is Context, Ownable, ERC20 {

// External functions

function setMaxSupply(uint256 newMaxSupply) external onlyOwner {
function setMaxSupply(
uint256 newMaxSupply
) external onlyCommunityOwnerOrTokenMaster {
if (newMaxSupply < totalSupply()) {
revert CommunityERC20_MaxSupplyLowerThanTotalSupply();
}
Expand All @@ -45,7 +48,10 @@ contract CommunityERC20 is Context, Ownable, ERC20 {
* an amount specified in `amounts`.
*
*/
function mintTo(address[] memory addresses, uint256[] memory amounts) external onlyOwner {
function mintTo(
address[] memory addresses,
uint256[] memory amounts
) external onlyCommunityOwnerOrTokenMaster {
if (addresses.length != amounts.length) {
revert CommunityERC20_MismatchingAddressesAndAmountsLengths();
}
Expand Down
22 changes: 18 additions & 4 deletions contracts/tokens/OwnerToken.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: Mozilla Public License 2.0
pragma solidity ^0.8.17;

import { BaseToken } from "./BaseToken.sol";
import {BaseToken} from "./BaseToken.sol";
import {CommunityOwnable} from "../CommunityOwnable.sol";

contract OwnerToken is BaseToken {
bytes public signerPublicKey;
Expand All @@ -13,19 +14,32 @@ contract OwnerToken is BaseToken {
address _receiver,
bytes memory _signerPublicKey
)
BaseToken(_name, _symbol, 1, false, true, _baseTokenURI, address(this), address(this))
BaseToken(
_name,
_symbol,
1,
false,
true,
_baseTokenURI,
address(this),
address(this)
)
{
signerPublicKey = _signerPublicKey;
address[] memory addresses = new address[](1);
addresses[0] = _receiver;
_mintTo(addresses);
}

function setMaxSupply(uint256 _newMaxSupply) external override onlyOwner {
function setMaxSupply(
uint256 _newMaxSupply
) external override onlyCommunityOwnerOrTokenMaster {
revert("max supply locked");
}

function setSignerPublicKey(bytes memory _newSignerPublicKey) external onlyOwner {
function setSignerPublicKey(
bytes memory _newSignerPublicKey
) external onlyCommunityOwnerOrTokenMaster {
signerPublicKey = _newSignerPublicKey;
}
}
18 changes: 9 additions & 9 deletions script/DeployOwnerAndMasterToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ contract DeployOwnerAndMasterToken is BaseScript {

vm.startBroadcast(broadcaster);
OwnerToken ownerToken = new OwnerToken(
ownerTokenConfig.name,
ownerTokenConfig.symbol,
ownerTokenConfig.baseURI,
broadcaster,
ownerTokenConfig.signerPublicKey
ownerTokenConfig.name,
ownerTokenConfig.symbol,
ownerTokenConfig.baseURI,
broadcaster,
ownerTokenConfig.signerPublicKey
);

MasterToken masterToken = new MasterToken(
masterTokenConfig.name,
masterTokenConfig.symbol,
masterTokenConfig.baseURI,
address(ownerToken)
masterTokenConfig.name,
masterTokenConfig.symbol,
masterTokenConfig.baseURI,
address(ownerToken)
);
vm.stopBroadcast();

Expand Down
4 changes: 3 additions & 1 deletion script/DeploymentConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ contract DeploymentConfig is Script {
address public immutable deployer;

constructor(address _broadcaster) {
if (_broadcaster == address(0)) revert DeploymentConfig_InvalidDeployerAddress();
if (_broadcaster == address(0)) {
revert DeploymentConfig_InvalidDeployerAddress();
}
deployer = _broadcaster;
if (block.chainid == 31_337) {
(ownerTokenConfig, masterTokenConfig) = getOrCreateAnvilEthConfig();
Expand Down
7 changes: 4 additions & 3 deletions test/CollectibleV1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.17;
import { Test } from "forge-std/Test.sol";
import { DeployOwnerAndMasterToken } from "../script/DeployOwnerAndMasterToken.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { CommunityOwnable } from "../contracts/CommunityOwnable.sol";
import { BaseToken } from "../contracts/tokens/BaseToken.sol";
import { OwnerToken } from "../contracts/tokens/OwnerToken.sol";
import { MasterToken } from "../contracts/tokens/MasterToken.sol";
Expand Down Expand Up @@ -31,7 +32,7 @@ contract CollectibleV1Test is Test {
name,
symbol,
maxSupply,
remoteBurnable,
remoteBurnable,
transferable,
baseURI,
address(ownerToken),
Expand Down Expand Up @@ -60,7 +61,7 @@ contract MintToTest is CollectibleV1Test {
}

function test_RevertWhen_SenderIsNotOwner() public {
vm.expectRevert(BaseToken.BaseToken_NotAuthorized.selector);
vm.expectRevert(CommunityOwnable.CommunityOwnable_NotAuthorized.selector);
collectibleV1.mintTo(accounts);
}

Expand Down Expand Up @@ -98,7 +99,7 @@ contract RemoteBurnTest is CollectibleV1Test {
function test_RevertWhen_SenderIsNotOwner() public {
uint256[] memory ids = new uint256[](1);
ids[0] = 0;
vm.expectRevert(BaseToken.BaseToken_NotAuthorized.selector);
vm.expectRevert(CommunityOwnable.CommunityOwnable_NotAuthorized.selector);
collectibleV1.remoteBurn(ids);
}

Expand Down
Loading

0 comments on commit 5763d7d

Please sign in to comment.