diff --git a/contracts/token/ERC721/ERC721TypedUpgradeable.sol b/contracts/token/ERC721/ERC721TypedUpgradeable.sol index c644712..c6c05ea 100644 --- a/contracts/token/ERC721/ERC721TypedUpgradeable.sol +++ b/contracts/token/ERC721/ERC721TypedUpgradeable.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.0; import { IERC721Metadata, IERC721Burnable, IERC721Typed } from "./IERC721.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; -import { ERC721URIStorageUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; +import { EcoERC721Base } from "./EcoERC721Base.sol"; import { ERC721SequencialMintUpbradeable } from "./ERC721SequencialMintUpbradeable.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; @@ -38,7 +38,7 @@ abstract contract ERC721TypedUpgradeable is IERC721Typed, ERC721SequencialMintUp function tokenURI( uint256 tokenId - ) public view virtual override(IERC721Metadata, ERC721Upgradeable) returns (string memory) { + ) public view virtual override(EcoERC721Base, IERC721Metadata) returns (string memory) { string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? baseURI.concat(tokenType(tokenId).toString()).concat(".json") : ""; diff --git a/contracts/token/ERC721/EcoERC721Base.sol b/contracts/token/ERC721/EcoERC721Base.sol index db67784..0a5352e 100644 --- a/contracts/token/ERC721/EcoERC721Base.sol +++ b/contracts/token/ERC721/EcoERC721Base.sol @@ -5,23 +5,31 @@ pragma solidity ^0.8.0; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import { IERC721Burnable } from "./IERC721.sol"; +import { IEcoERC721Base, IERC721Burnable, IERC721Metadata } from "./IERC721.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { ERC721PausableUpgradeable } from "./ERC721PausableUpgradeable.sol"; import { ERC721BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; +import { EcoERC721URIStorageUpgradeable, ERC721URIStorageUpgradeable } from "./EcoERC721URIStorageUpgradeable.sol"; import { ERC721EnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; -import { ERC721PausableUpgradeable } from "./ERC721PausableUpgradeable.sol"; abstract contract EcoERC721Base is - IERC721Burnable, + IEcoERC721Base, ERC721Upgradeable, + ERC721PausableUpgradeable, ERC721BurnableUpgradeable, - ERC721EnumerableUpgradeable, - ERC721PausableUpgradeable + EcoERC721URIStorageUpgradeable, + ERC721EnumerableUpgradeable { function supportsInterface( bytes4 interfaceId - ) public view virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable, IERC165) returns (bool) { + ) + public + view + virtual + override(ERC721EnumerableUpgradeable, ERC721Upgradeable, EcoERC721URIStorageUpgradeable, IERC165) + returns (bool) + { return super.supportsInterface(interfaceId); } @@ -48,4 +56,16 @@ abstract contract EcoERC721Base is ) internal virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable) { return super._increaseBalance(account, amount); } + + function tokenURI( + uint256 tokenId + ) + public + view + virtual + override(ERC721Upgradeable, EcoERC721URIStorageUpgradeable, IERC721Metadata) + returns (string memory) + { + return super.tokenURI(tokenId); + } } diff --git a/contracts/token/ERC721/EcoERC721URIStorageUpgradeable.sol b/contracts/token/ERC721/EcoERC721URIStorageUpgradeable.sol new file mode 100644 index 0000000..cf84815 --- /dev/null +++ b/contracts/token/ERC721/EcoERC721URIStorageUpgradeable.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Pausable.sol) + +pragma solidity ^0.8.20; + +import { SelectorRoleControlUpgradeable, AccessControlEnumerableUpgradeable } from "../../access/SelectorRoleControlUpgradeable.sol"; + +import { IERC165, IERC721Metadata, IEcoERC721URIStorage } from "./IERC721.sol"; +import { ERC721URIStorageUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; + +abstract contract EcoERC721URIStorageUpgradeable is + IEcoERC721URIStorage, + SelectorRoleControlUpgradeable, + ERC721URIStorageUpgradeable +{ + function supportsInterface( + bytes4 interfaceId + ) + public + view + virtual + override(AccessControlEnumerableUpgradeable, ERC721URIStorageUpgradeable, IERC165) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function tokenURI( + uint256 tokenId + ) public view virtual override(ERC721URIStorageUpgradeable, IERC721Metadata) returns (string memory) { + return string.concat(super.tokenURI(tokenId), ".json"); + } + + function setTokenURI(uint256 tokenId, string memory _tokenURI) public virtual override onlyAdmin { + return _setTokenURI(tokenId, _tokenURI); + } +} diff --git a/contracts/token/ERC721/IERC721.sol b/contracts/token/ERC721/IERC721.sol index add427c..96d7935 100644 --- a/contracts/token/ERC721/IERC721.sol +++ b/contracts/token/ERC721/IERC721.sol @@ -13,13 +13,19 @@ import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Rec import { ISelectorRoleControl } from "../../access/SelectorRoleControlUpgradeable.sol"; -interface IEcoERC721Base is IERC165, IERC721, IERC4906, IERC721Metadata, IERC721Errors {} +interface IERC721Base is IERC165, IERC721, IERC4906, IERC721Metadata, IERC721Errors {} -interface IERC721Burnable is IEcoERC721Base { +interface IERC721Burnable is IERC721Base { function burn(uint256 tokenId) external; } -interface IERC721SequencialMintUpbradeable is ISelectorRoleControl, IERC721Burnable { +interface IEcoERC721URIStorage is IERC721Base { + function setTokenURI(uint256 tokenId, string memory _tokenURI) external; +} + +interface IEcoERC721Base is IERC721Burnable, IEcoERC721URIStorage {} + +interface IERC721SequencialMintUpbradeable is ISelectorRoleControl, IEcoERC721Base { function nextMintId() external view returns (uint256 tokeId); function nextMint(address to) external returns (uint256 tokenId); @@ -55,4 +61,4 @@ interface IERC721Queryable is IERC721SequencialMintUpbradeable { function tokensOfOwner(address owner) external view returns (uint256[] memory); } -interface IEcoERC721 is IERC721SequencialMintUpbradeable {} +interface IEcoERC721 is IERC721Queryable {} diff --git a/contracts/token/NFT/NFT_Mintable.sol b/contracts/token/NFT/NFT_Mintable.sol index 6b333d0..7eb0fad 100644 --- a/contracts/token/NFT/NFT_Mintable.sol +++ b/contracts/token/NFT/NFT_Mintable.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.0; -import { IERC721Burnable, IERC721Queryable } from "../ERC721/IERC721.sol"; +import { IEcoERC721, IERC721Burnable, IERC721Queryable } from "../ERC721/IERC721.sol"; import { ERC721SequencialMintUpbradeable } from "../ERC721/ERC721SequencialMintUpbradeable.sol"; import { ERC721QueryableUpgradeable } from "../ERC721/ERC721QueryableUpgradeable.sol"; -interface INFT_Mintable is IERC721Queryable {} +interface INFT_Mintable is IEcoERC721 {} // INFT_Mintable, contract NFT_Mintable is INFT_Mintable, ERC721SequencialMintUpbradeable, ERC721QueryableUpgradeable { @@ -17,6 +17,11 @@ contract NFT_Mintable is INFT_Mintable, ERC721SequencialMintUpbradeable, ERC721Q __ERC721Burnable_init(); __ERC721Enumerable_init(); __Pausable_init(); + _checkBaseURI(); + } + + function _checkBaseURI() internal view virtual { + require(bytes(_baseURI()).length != 0); } function burn( diff --git a/contracts/token/NFT/NFT_Typed.sol b/contracts/token/NFT/NFT_Typed.sol index abd8502..5c83eb7 100644 --- a/contracts/token/NFT/NFT_Typed.sol +++ b/contracts/token/NFT/NFT_Typed.sol @@ -10,6 +10,7 @@ import { INFT_Mintable, NFT_Mintable } from "./NFT_Mintable.sol"; import { SelectorRoleControlUpgradeable } from "../../access/SelectorRoleControlUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { EcoERC721Base } from "../ERC721/EcoERC721Base.sol"; import { ERC721SequencialMintUpbradeable } from "../ERC721/ERC721SequencialMintUpbradeable.sol"; import { ERC721TypedUpgradeable } from "../ERC721/ERC721TypedUpgradeable.sol"; @@ -22,6 +23,8 @@ contract NFT_Typed is INFT_Typed, NFT_Mintable, ERC721TypedUpgradeable { return super.supportsInterface(interfaceId); } + function _checkBaseURI() internal view override {} + function _baseURI() internal view @@ -34,7 +37,7 @@ contract NFT_Typed is INFT_Typed, NFT_Mintable, ERC721TypedUpgradeable { function tokenURI( uint256 tokenId - ) public view virtual override(IERC721Metadata, ERC721Upgradeable, ERC721TypedUpgradeable) returns (string memory) { + ) public view virtual override(IERC721Metadata, EcoERC721Base, ERC721TypedUpgradeable) returns (string memory) { return super.tokenURI(tokenId); } diff --git a/contracts/token/NFT/SBT.sol b/contracts/token/NFT/SBT.sol index a67ee11..e506470 100644 --- a/contracts/token/NFT/SBT.sol +++ b/contracts/token/NFT/SBT.sol @@ -7,6 +7,7 @@ import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import { IERC721Metadata } from "../ERC721/IERC721.sol"; import { EcoERC721Base } from "../ERC721/EcoERC721Base.sol"; import { ERC721SequencialMintUpbradeable } from "../ERC721/ERC721SequencialMintUpbradeable.sol"; import { ERC721SoulBoundUpgradeable } from "../ERC721/ERC721SoulBoundUpgradeable.sol"; @@ -34,4 +35,10 @@ contract SBT is NFT_Mintable, ERC721SoulBoundUpgradeable { ) internal virtual override(ERC721SoulBoundUpgradeable, EcoERC721Base) returns (address) { return super._update(to, tokenId, auth); } + + function tokenURI( + uint256 tokenId + ) public view virtual override(ERC721Upgradeable, EcoERC721Base, IERC721Metadata) returns (string memory) { + return super.tokenURI(tokenId); + } } diff --git a/contracts/token/NFT/test.sol b/contracts/token/NFT/test.sol new file mode 100644 index 0000000..a78105f --- /dev/null +++ b/contracts/token/NFT/test.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.0; + +import { EcoERC721Base } from "../ERC721/EcoERC721Base.sol"; + +import { NFT_Mintable } from "./NFT_Mintable.sol"; +import { NFT_Typed } from "./NFT_Typed.sol"; +import { SBT } from "./SBT.sol"; + +contract Test_NFT_Mintable is NFT_Mintable { + function _baseURI() internal pure override returns (string memory) { + return "https://test.com/"; + } +} + +contract Test_NFT_Typed is NFT_Typed { + +} // already provide base uri + +contract Test_SBT is SBT { + function _baseURI() internal pure override returns (string memory) { + return "https://test.com/"; + } +} diff --git a/test/token/NFT/NFT_Mintable.ts b/test/token/NFT/NFT_Mintable.ts index c5762e0..7931545 100644 --- a/test/token/NFT/NFT_Mintable.ts +++ b/test/token/NFT/NFT_Mintable.ts @@ -11,7 +11,7 @@ describe("NFT Mintable", function () { async function NFT_Mintable_Fixture() { const [owner, admin, user0, user1] = await hre.ethers.getSigners(); - const NFT = await hre.ethers.getContractFactory("NFT_Mintable"); + const NFT = await hre.ethers.getContractFactory("Test_NFT_Mintable"); const nft = await NFT.connect(owner).deploy(); await nft.initNFT_Mintable(owner.address, name, symbol); diff --git a/test/token/NFT/NFT_Typed.ts b/test/token/NFT/NFT_Typed.ts index f4c3a0e..eeb5bc9 100644 --- a/test/token/NFT/NFT_Typed.ts +++ b/test/token/NFT/NFT_Typed.ts @@ -9,7 +9,7 @@ describe("NFT Typed", function () { async function NFT_Typed_Fixture() { const [owner, admin, user0, user1] = await hre.ethers.getSigners(); - const NFT = await hre.ethers.getContractFactory("NFT_Typed"); + const NFT = await hre.ethers.getContractFactory("Test_NFT_Typed"); const nft = await NFT.connect(owner).deploy(); await nft.initNFT_Mintable(owner.address, name, symbol); diff --git a/test/token/NFT/SBT.ts b/test/token/NFT/SBT.ts index 07957f1..ddfce35 100644 --- a/test/token/NFT/SBT.ts +++ b/test/token/NFT/SBT.ts @@ -11,7 +11,7 @@ describe("SBT", function () { async function SBT_Mintable_Fixture() { const [owner, admin, user0, user1] = await hre.ethers.getSigners(); - const SBT = await hre.ethers.getContractFactory("SBT"); + const SBT = await hre.ethers.getContractFactory("Test_SBT"); const sbt = await SBT.connect(owner).deploy(); await sbt.initNFT_Mintable(owner.address, name, symbol);