diff --git a/contracts/token/ERC1155/ERC1155MintableUpgradeable.sol b/contracts/token/ERC1155/ERC1155MintableUpgradeable.sol index 0dca36b..255d0bb 100644 --- a/contracts/token/ERC1155/ERC1155MintableUpgradeable.sol +++ b/contracts/token/ERC1155/ERC1155MintableUpgradeable.sol @@ -4,37 +4,18 @@ pragma solidity ^0.8.0; // import { ISelectorRoleControl, IPausable, IEcoOwnable, import { AccessControlEnumerableUpgradeable, SelectorRoleControlUpgradeable } from "../../access/SelectorRoleControlUpgradeable.sol"; - import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { ERC1155Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; -import { ERC1155BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol"; -import { ERC1155PausableUpgradeable } from "./ERC1155PausableUpgradeable.sol"; - import { IERC1155Mintable } from "./IERC1155.sol"; -abstract contract ERC1155MintableUpgradeable is - SelectorRoleControlUpgradeable, - IERC1155Mintable, - ERC1155Upgradeable, - ERC1155BurnableUpgradeable, - ERC1155PausableUpgradeable -{ +abstract contract ERC1155MintableUpgradeable is SelectorRoleControlUpgradeable, IERC1155Mintable, ERC1155Upgradeable { function supportsInterface( bytes4 interfaceId ) public view virtual override(IERC165, AccessControlEnumerableUpgradeable, ERC1155Upgradeable) returns (bool) { return super.supportsInterface(interfaceId); } - function _update( - address from, - address to, - uint256[] memory ids, - uint256[] memory values - ) internal virtual override(ERC1155Upgradeable, ERC1155PausableUpgradeable) whenNotPaused { - super._update(from, to, ids, values); - } - function mint(address to, uint256 id, uint256 value, bytes calldata data) external onlyAdmin { _mint(to, id, value, data); } diff --git a/contracts/token/ERC1155/EcoERC1155.sol b/contracts/token/ERC1155/EcoERC1155.sol deleted file mode 100644 index 014cb66..0000000 --- a/contracts/token/ERC1155/EcoERC1155.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import { IERC1155MetadataURI, IEcoERC1155 } from "./IERC1155.sol"; -import { ERC1155MintableUpgradeable } from "./ERC1155MintableUpgradeable.sol"; -import { EcoERC1155URIControl } from "./EcoERC1155URIControl.sol"; - -import { ERC1155URIStorageUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155URIStorageUpgradeable.sol"; - -import { ERC1155Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; - -contract EcoERC1155 is ERC1155MintableUpgradeable, EcoERC1155URIControl { - function supportsInterface( - bytes4 interfaceId - ) public view override(ERC1155MintableUpgradeable, EcoERC1155URIControl) returns (bool) { - return super.supportsInterface(interfaceId); - } - - function _update( - address from, - address to, - uint256[] memory ids, - uint256[] memory values - ) internal virtual override(ERC1155MintableUpgradeable, ERC1155Upgradeable) { - super._update(from, to, ids, values); - } - - function uri( - uint256 id - ) - public - view - virtual - override(ERC1155URIStorageUpgradeable, ERC1155Upgradeable, IERC1155MetadataURI) - returns (string memory) - { - return super.uri(id); - } -} diff --git a/contracts/token/ERC1155/EcoERC1155Upgradeable.sol b/contracts/token/ERC1155/EcoERC1155Upgradeable.sol new file mode 100644 index 0000000..62f9525 --- /dev/null +++ b/contracts/token/ERC1155/EcoERC1155Upgradeable.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { IERC1155MetadataURI, IEcoERC1155, IERC1155Supply } from "./IERC1155.sol"; + +import { ERC1155Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import { ERC1155MintableUpgradeable } from "./ERC1155MintableUpgradeable.sol"; + +import { ERC1155URIStorageUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155URIStorageUpgradeable.sol"; +import { EcoERC1155URIControl } from "./EcoERC1155URIControl.sol"; + +import { ERC1155BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol"; +import { ERC1155SupplyUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; +import { ERC1155PausableUpgradeable } from "./ERC1155PausableUpgradeable.sol"; + +contract EcoERC1155Upgradeable is + IEcoERC1155, + ERC1155MintableUpgradeable, + EcoERC1155URIControl, + ERC1155BurnableUpgradeable, + ERC1155PausableUpgradeable, + ERC1155SupplyUpgradeable +{ + function initEcoERC1155(address initialOwner, string memory baseURI) public initializer { + _initEcoOwnable(initialOwner); + __ERC1155_init(baseURI); + } + + function supportsInterface( + bytes4 interfaceId + ) + public + view + override(ERC1155MintableUpgradeable, ERC1155Upgradeable, EcoERC1155URIControl, IERC165) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function _update( + address from, + address to, + uint256[] memory ids, + uint256[] memory values + ) internal virtual override(ERC1155PausableUpgradeable, ERC1155SupplyUpgradeable, ERC1155Upgradeable) { + super._update(from, to, ids, values); + } + + function uri( + uint256 id + ) + public + view + virtual + override(ERC1155URIStorageUpgradeable, ERC1155Upgradeable, IERC1155MetadataURI) + returns (string memory) + { + return super.uri(id); + } + + function totalSupply(uint256 id) public view override(ERC1155SupplyUpgradeable, IERC1155Supply) returns (uint256) { + return super.totalSupply(id); + } + + function totalSupply() public view override(ERC1155SupplyUpgradeable, IERC1155Supply) returns (uint256) { + return super.totalSupply(); + } + + function exists(uint256 id) public view override(ERC1155SupplyUpgradeable, IERC1155Supply) returns (bool) { + return super.exists(id); + } +} diff --git a/contracts/token/ERC1155/IERC1155.sol b/contracts/token/ERC1155/IERC1155.sol index 7fcea80..b2834c8 100644 --- a/contracts/token/ERC1155/IERC1155.sol +++ b/contracts/token/ERC1155/IERC1155.sol @@ -13,10 +13,18 @@ interface IERC1155URIControl is ISelectorRoleControl, IERC1155MetadataURI { function setBaseURI(string memory baseURI) external; } +interface IERC1155Supply is IERC1155 { + function totalSupply(uint256 id) external view returns (uint256); + + function totalSupply() external view returns (uint256); + + function exists(uint256 id) external view returns (bool); +} + interface IERC1155Mintable is ISelectorRoleControl, IERC1155 { function mint(address to, uint256 id, uint256 value, bytes calldata data) external; function mintBatch(address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external; } -interface IEcoERC1155 is IERC1155Mintable, IERC1155URIControl {} +interface IEcoERC1155 is IERC1155Supply, IERC1155Mintable, IERC1155URIControl {} diff --git a/test/token/ERC1155.ts b/test/token/ERC1155.ts new file mode 100644 index 0000000..af71e90 --- /dev/null +++ b/test/token/ERC1155.ts @@ -0,0 +1,85 @@ +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { expect } from "chai"; +import hre from "hardhat"; + +import { getSelector } from "../helper"; + +describe("ERC1155 Mintable", function () { + const baseURI = "https://this.is.base.uri/"; + const amount = hre.ethers.parseEther("100"); + + const default1155Id = 0; + + async function NFT_Mintable_Fixture() { + const [owner, ...users] = await hre.ethers.getSigners(); + + const ERC1155 = await hre.ethers.getContractFactory("EcoERC1155Upgradeable"); + const erc1155 = await ERC1155.deploy(); + await erc1155.initEcoERC1155(owner, baseURI); + + return { erc1155, owner, users }; + } + + describe("Deployment", function () { + it("Should set the right owner", async function () { + const { erc1155, owner } = await loadFixture(NFT_Mintable_Fixture); + + expect(await erc1155.owner()).to.equal(owner.address); + await expect(erc1155.initEcoERC1155(owner, baseURI)).reverted; + }); + + it("Should set the right metadata", async function () { + const { erc1155 } = await loadFixture(NFT_Mintable_Fixture); + + expect(await erc1155.uri(0)).to.equal(baseURI); + }); + }); + + describe("ERC1155 Feature", function () { + describe("Mint", function () { + it("Should revert with the right error if mint called from another account", async function () { + const { erc1155, users } = await loadFixture(NFT_Mintable_Fixture); + const user_connected_nft = erc1155.connect(users[0]); + await expect(user_connected_nft.mint(users[0], default1155Id, amount, "0x")).reverted; + }); + + it("Shouldn't fail mint with the right owner", async function () { + const { erc1155, users } = await loadFixture(NFT_Mintable_Fixture); + await expect(erc1155.mint(users[0], default1155Id, amount, "0x")).not.reverted; + }); + + it("Shouldn't fail mint with the right role access account", async function () { + const { erc1155, users } = await loadFixture(NFT_Mintable_Fixture); + + await expect(erc1155.grantSelectorRole(getSelector(erc1155.mint), users[0])).not.reverted; + + const user_connected_nft = erc1155.connect(users[0]); + await expect(user_connected_nft.mint(users[0], default1155Id, amount, "0x")).not.reverted; + + await expect(erc1155.revokeSelectorRole(getSelector(erc1155.mint), users[0])).not.reverted; + await expect(user_connected_nft.mint(users[0], default1155Id, amount, "0x")).reverted; + }); + }); + + describe("Transfer", function () { + it("Should revert with the right error if mint called from another account", async function () { + const { owner, erc1155, users } = await loadFixture(NFT_Mintable_Fixture); + await expect(erc1155.mint(users[0], default1155Id, amount, "0x")).not.reverted; + await expect(erc1155.connect(users[0]).burn(users[0], default1155Id, amount)).not.reverted; + expect(await erc1155.balanceOf(users[0], default1155Id)).equal(0); + + await expect(erc1155.mint(users[0], default1155Id, amount, "0x")).not.reverted; + await expect(erc1155.mint(users[0], default1155Id, amount, "0x")).not.reverted; + + await erc1155.connect(users[0]).setApprovalForAll(owner, true); + await expect(erc1155.safeTransferFrom(users[0], users[1], default1155Id, await erc1155.balanceOf(users[0], default1155Id), "0x")).not.reverted; + await expect(erc1155.safeTransferFrom(users[0], users[1], default1155Id, amount, "0x")).reverted; + + await expect(erc1155.connect(users[1]).safeTransferFrom(users[1], users[0], default1155Id, await erc1155.balanceOf(users[1], default1155Id)/2n, "0x")) + .not.reverted; // this allowed, openzeppelin transfer imple differ from erc20 + await erc1155.connect(users[1]).setApprovalForAll(owner, true); + await expect(erc1155.burn(users[1], default1155Id, await erc1155.balanceOf(users[1], default1155Id))).not.reverted; + }); + }); + }); +}); diff --git a/test/token/ERC20.ts b/test/token/ERC20.ts index 7cd9b11..73af5b9 100644 --- a/test/token/ERC20.ts +++ b/test/token/ERC20.ts @@ -78,7 +78,7 @@ describe("ERC20 Mintable", function () { await expect(erc20.transferFrom(users[0], users[1], await erc20.balanceOf(users[0]))).not.reverted; await expect(erc20.transferFrom(users[0], users[1], amount)).reverted; await expect(erc20.connect(users[1]).transferFrom(users[1], users[0], await erc20.balanceOf(users[1]))) - .reverted; + .reverted; // should use transfer, owner == spender not allowed await erc20.connect(users[1]).approve(owner, hre.ethers.MaxUint256); await expect(erc20.burnFrom(users[1], await erc20.balanceOf(users[1]))).not.reverted; });