From ba7f7c77970c7e2d81e020b0ad4211f3d2f25dc8 Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 17:23:44 +0100 Subject: [PATCH 1/7] v1.3.3 --- package.json | 2 +- pom.xml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b9c58402..c393e79a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nevermined-io/contracts", - "version": "1.3.2", + "version": "1.3.3", "description": "Nevermined implementation of Nevermined in Solidity", "bugs": { "url": "https://github.com/nevermined-io/contracts/issues" diff --git a/pom.xml b/pom.xml index 98c86690..e46723ef 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.keyko.nevermined contracts jar - 1.3.2 + 1.3.3 Nevermined Contracts Nevermined Data Platform Smart Contracts in Solidity https://github.com/nevermined-io/contracts diff --git a/setup.py b/setup.py index 6eaac8a3..b86be491 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/nevermined-io/contracts', - version='1.3.2', + version='1.3.3', zip_safe=False, ) From 04a0d0c002fe68402b407d3b34244b69a67d1906 Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 17:24:16 +0100 Subject: [PATCH 2/7] Adding support to EIP-2981 --- contracts/registry/DIDRegistry.sol | 6 +- contracts/registry/DIDRegistryLibrary.sol | 2 +- contracts/token/erc1155/NFTUpgradeable.sol | 36 +++++++++-- contracts/token/erc2981/ERC2981.sol | 63 ++++++++++++++++++++ contracts/token/erc721/NFT721Upgradeable.sol | 39 ++++++++++-- test/unit/registry/Mintable721DIDRegistry.js | 17 ++++++ test/unit/registry/MintableDIDRegistry.js | 14 +++++ 7 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 contracts/token/erc2981/ERC2981.sol diff --git a/contracts/registry/DIDRegistry.sol b/contracts/registry/DIDRegistry.sol index 0986772a..e05d2637 100644 --- a/contracts/registry/DIDRegistry.sol +++ b/contracts/registry/DIDRegistry.sol @@ -146,7 +146,9 @@ contract DIDRegistry is DIDFactory { returns (bool success) { didRegisterList.initializeNftConfig(_did, _cap, _royalties); - + + erc1155.setTokenRoyalty(uint256(_did), msg.sender, _royalties); + if (_mint) { mint(_did, _cap); } @@ -167,6 +169,8 @@ contract DIDRegistry is DIDFactory { { didRegisterList.initializeNft721Config(_did, _royalties); + erc721.setTokenRoyalty(uint256(_did), msg.sender, _royalties); + if (_mint) { mint721(_did); } diff --git a/contracts/registry/DIDRegistryLibrary.sol b/contracts/registry/DIDRegistryLibrary.sol index 9fe4a6a3..a5a725f1 100644 --- a/contracts/registry/DIDRegistryLibrary.sol +++ b/contracts/registry/DIDRegistryLibrary.sol @@ -120,7 +120,7 @@ library DIDRegistryLibrary { require(!_self.didRegisters[_did].nftInitialized, 'NFT already initialized'); - require(_royalties < 100, 'Invalid royalties number'); + require(_royalties <= 100, 'Invalid royalties number'); require(_royalties >= _self.didRegisters[_did].royalties, 'Cannot decrease royalties'); _self.didRegisters[_did].mintCap = _cap; diff --git a/contracts/token/erc1155/NFTUpgradeable.sol b/contracts/token/erc1155/NFTUpgradeable.sol index 45b7aa8f..5ea81e55 100644 --- a/contracts/token/erc1155/NFTUpgradeable.sol +++ b/contracts/token/erc1155/NFTUpgradeable.sol @@ -5,17 +5,18 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import "../erc2981/ERC2981.sol"; /** * * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 */ -contract NFTUpgradeable is ERC1155Upgradeable, OwnableUpgradeable, AccessControlUpgradeable { +contract NFTUpgradeable is ERC1155Upgradeable, ERC2981, OwnableUpgradeable, AccessControlUpgradeable { // Mapping from account to proxy approvals mapping (address => bool) private _proxyApprovals; - + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); /** @@ -35,7 +36,6 @@ contract NFTUpgradeable is ERC1155Upgradeable, OwnableUpgradeable, AccessControl AccessControlUpgradeable.__AccessControl_init(); AccessControlUpgradeable._setupRole(MINTER_ROLE, msg.sender); } - function setProxyApproval(address operator, bool approved) public onlyOwner virtual { _proxyApprovals[operator] = approved; @@ -63,9 +63,35 @@ contract NFTUpgradeable is ERC1155Upgradeable, OwnableUpgradeable, AccessControl AccessControlUpgradeable._setupRole(MINTER_ROLE, account); } - function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlUpgradeable, ERC1155Upgradeable) returns (bool) { + /** + * @dev Record the asset royalties + * @param tokenId the id of the asset with the royalties associated + * @param receiver the receiver of the royalties (the original creator) + * @param royaltyAmount percentage (no decimals, between 0 and 100) + */ + function setTokenRoyalty( + uint256 tokenId, + address receiver, + uint256 royaltyAmount + ) + public + { + require(hasRole(MINTER_ROLE, msg.sender), 'only minter'); + _setTokenRoyalty(tokenId, receiver, royaltyAmount); + } + + function supportsInterface( + bytes4 interfaceId + ) + public + view + virtual + override(AccessControlUpgradeable, ERC1155Upgradeable, ERC2981) + returns (bool) + { return AccessControlUpgradeable.supportsInterface(interfaceId) - || ERC1155Upgradeable.supportsInterface(interfaceId); + || ERC1155Upgradeable.supportsInterface(interfaceId) + || ERC2981.supportsInterface(interfaceId); } } diff --git a/contracts/token/erc2981/ERC2981.sol b/contracts/token/erc2981/ERC2981.sol new file mode 100644 index 00000000..01946f3a --- /dev/null +++ b/contracts/token/erc2981/ERC2981.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.8.0; +// Copyright 2020 Keyko GmbH. +// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +// Code is Apache-2.0 and docs are CC-BY-4.0 + +import '@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol'; + +/** + * + * @dev Implementation of the Royalties EIP-2981 base contract + * See https://eips.ethereum.org/EIPS/eip-2981 + */ +abstract contract ERC2981 is IERC2981Upgradeable { + + struct RoyaltyInfo { + address receiver; + uint256 royaltyAmount; + } + + // Mapping of Royalties per tokenId (DID) + mapping(uint256 => RoyaltyInfo) internal _royalties; + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override + returns (bool) + { + return + interfaceId == type(IERC2981Upgradeable).interfaceId || + supportsInterface(interfaceId); + } + + function _setTokenRoyalty( + uint256 tokenId, + address receiver, + uint256 royaltyAmount + ) + internal + { + require(royaltyAmount <= 100, 'ERC2981Royalties: Too high'); + _royalties[tokenId] = RoyaltyInfo(receiver, royaltyAmount); + } + + /** + * @inheritdoc IERC2981Upgradeable + */ + function royaltyInfo( + uint256 tokenId, + uint256 value + ) + external + view + override + returns (address receiver, uint256 royaltyAmount) + { + RoyaltyInfo memory royalties = _royalties[tokenId]; + receiver = royalties.receiver; + royaltyAmount = (value * royalties.royaltyAmount) / 100; + } + +} diff --git a/contracts/token/erc721/NFT721Upgradeable.sol b/contracts/token/erc721/NFT721Upgradeable.sol index b472eff2..5c38012a 100644 --- a/contracts/token/erc721/NFT721Upgradeable.sol +++ b/contracts/token/erc721/NFT721Upgradeable.sol @@ -5,12 +5,13 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import "../erc2981/ERC2981.sol"; /** * * @dev Implementation of the basic standard multi-token. */ -contract NFT721Upgradeable is ERC721Upgradeable, OwnableUpgradeable, AccessControlUpgradeable { +contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, AccessControlUpgradeable { // Mapping from account to proxy approvals mapping (address => bool) private _proxyApprovals; @@ -47,7 +48,11 @@ contract NFT721Upgradeable is ERC721Upgradeable, OwnableUpgradeable, AccessContr function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { return super.isApprovedForAll(account, operator) || _proxyApprovals[operator]; } - + + function addMinter(address account) public onlyOwner { + AccessControlUpgradeable._setupRole(MINTER_ROLE, account); + } + function mint(address to, uint256 id) public { require(hasRole(MINTER_ROLE, msg.sender), 'only minter can mint'); _mint(to, id); @@ -58,13 +63,35 @@ contract NFT721Upgradeable is ERC721Upgradeable, OwnableUpgradeable, AccessContr _burn(id); } - function addMinter(address account) public onlyOwner { - AccessControlUpgradeable._setupRole(MINTER_ROLE, account); + /** + * @dev Record the asset royalties + * @param tokenId the id of the asset with the royalties associated + * @param receiver the receiver of the royalties (the original creator) + * @param royaltyAmount percentage (no decimals, between 0 and 100) + */ + function setTokenRoyalty( + uint256 tokenId, + address receiver, + uint256 royaltyAmount + ) + public + { + require(hasRole(MINTER_ROLE, msg.sender), 'only minter'); + _setTokenRoyalty(tokenId, receiver, royaltyAmount); } - function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlUpgradeable, ERC721Upgradeable) returns (bool) { + function supportsInterface( + bytes4 interfaceId + ) + public + view + virtual + override(AccessControlUpgradeable, ERC721Upgradeable, ERC2981) + returns (bool) + { return AccessControlUpgradeable.supportsInterface(interfaceId) - || ERC721Upgradeable.supportsInterface(interfaceId); + || ERC721Upgradeable.supportsInterface(interfaceId) + || ERC2981.supportsInterface(interfaceId); } } diff --git a/test/unit/registry/Mintable721DIDRegistry.js b/test/unit/registry/Mintable721DIDRegistry.js index 5a1912e1..a60a49ef 100644 --- a/test/unit/registry/Mintable721DIDRegistry.js +++ b/test/unit/registry/Mintable721DIDRegistry.js @@ -114,6 +114,23 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await assert.isRejected(nft.ownerOf(did)) }) + it('The royalties should be initialized and retrieved (ERC-2981)', async () => { + const didSeed = testUtils.generateId() + const did = await didRegistry.hashDID(didSeed, owner) + const checksum = testUtils.generateId() + await didRegistry.registerAttribute( + didSeed, checksum, [], value, { from: owner }) + + await didRegistry.enableAndMintDidNft721(did, 10, true, { from: owner }) + + const nftOwner = await nft.ownerOf(did) + assert.strictEqual(owner, nftOwner) + + const { receiver, royaltyAmount } = await nft.royaltyInfo(did, 500) + assert.strictEqual(owner, receiver) + assert.strictEqual(50, royaltyAmount.toNumber()) + }) + it('Should Mint automatically if is configured that way', async () => { const didSeed = testUtils.generateId() const did = await didRegistry.hashDID(didSeed, owner) diff --git a/test/unit/registry/MintableDIDRegistry.js b/test/unit/registry/MintableDIDRegistry.js index fc0bb203..fa3f0c0b 100644 --- a/test/unit/registry/MintableDIDRegistry.js +++ b/test/unit/registry/MintableDIDRegistry.js @@ -128,6 +128,18 @@ contract('Mintable DIDRegistry', (accounts) => { assert.strictEqual(10, balance.toNumber()) }) + it('The royalties should be initialized and retrieved (ERC-2981)', async () => { + const didSeed = testUtils.generateId() + const did = await didRegistry.hashDID(didSeed, owner) + const checksum = testUtils.generateId() + await didRegistry.registerMintableDID( + didSeed, checksum, [], value, 999, 10, constants.activities.GENERATED, '', { from: owner }) + + const { receiver, royaltyAmount } = await nft.royaltyInfo(did, 500) + assert.strictEqual(owner, receiver) + assert.strictEqual(50, royaltyAmount.toNumber()) + }) + it('Should Mint automatically if is configured that way', async () => { const didSeed = testUtils.generateId() const did = await didRegistry.hashDID(didSeed, owner) @@ -141,6 +153,7 @@ contract('Mintable DIDRegistry', (accounts) => { assert.strictEqual(5, balanceOwner.toNumber()) }) + it('Should mint if is not capped', async () => { const didSeed = testUtils.generateId() const did = await didRegistry.hashDID(didSeed, owner) @@ -233,6 +246,7 @@ contract('Mintable DIDRegistry', (accounts) => { assert.isNotOk( // MUST BE FALSE. Original creator is not getting royalties await didRegistryLibraryProxy.areRoyaltiesValid(did, [100], [other])) + }) }) }) From 488e1423bf16818fa60e2d39f98f476b3ca16240 Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 17:48:58 +0100 Subject: [PATCH 3/7] linting --- contracts/token/erc1155/NFTUpgradeable.sol | 2 +- contracts/token/erc721/NFT721Upgradeable.sol | 2 +- test/unit/registry/MintableDIDRegistry.js | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/token/erc1155/NFTUpgradeable.sol b/contracts/token/erc1155/NFTUpgradeable.sol index 5ea81e55..002781fd 100644 --- a/contracts/token/erc1155/NFTUpgradeable.sol +++ b/contracts/token/erc1155/NFTUpgradeable.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; -import "../erc2981/ERC2981.sol"; +import '../erc2981/ERC2981.sol'; /** * diff --git a/contracts/token/erc721/NFT721Upgradeable.sol b/contracts/token/erc721/NFT721Upgradeable.sol index 5c38012a..4f594637 100644 --- a/contracts/token/erc721/NFT721Upgradeable.sol +++ b/contracts/token/erc721/NFT721Upgradeable.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; -import "../erc2981/ERC2981.sol"; +import '../erc2981/ERC2981.sol'; /** * diff --git a/test/unit/registry/MintableDIDRegistry.js b/test/unit/registry/MintableDIDRegistry.js index fa3f0c0b..b12a6328 100644 --- a/test/unit/registry/MintableDIDRegistry.js +++ b/test/unit/registry/MintableDIDRegistry.js @@ -153,7 +153,6 @@ contract('Mintable DIDRegistry', (accounts) => { assert.strictEqual(5, balanceOwner.toNumber()) }) - it('Should mint if is not capped', async () => { const didSeed = testUtils.generateId() const did = await didRegistry.hashDID(didSeed, owner) @@ -246,7 +245,6 @@ contract('Mintable DIDRegistry', (accounts) => { assert.isNotOk( // MUST BE FALSE. Original creator is not getting royalties await didRegistryLibraryProxy.areRoyaltiesValid(did, [100], [other])) - }) }) }) From 1800e52897af99263d9487c1ba0c70e863c1cb1b Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 18:26:21 +0100 Subject: [PATCH 4/7] initializing royalties just when are necessary --- contracts/registry/DIDRegistry.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/registry/DIDRegistry.sol b/contracts/registry/DIDRegistry.sol index e05d2637..df71aaf1 100644 --- a/contracts/registry/DIDRegistry.sol +++ b/contracts/registry/DIDRegistry.sol @@ -147,11 +147,11 @@ contract DIDRegistry is DIDFactory { { didRegisterList.initializeNftConfig(_did, _cap, _royalties); - erc1155.setTokenRoyalty(uint256(_did), msg.sender, _royalties); + if (_royalties > 0) + erc1155.setTokenRoyalty(uint256(_did), msg.sender, _royalties); - if (_mint) { + if (_mint) mint(_did, _cap); - } return super.used( keccak256(abi.encode(_did, _cap, _royalties, msg.sender)), @@ -169,11 +169,11 @@ contract DIDRegistry is DIDFactory { { didRegisterList.initializeNft721Config(_did, _royalties); - erc721.setTokenRoyalty(uint256(_did), msg.sender, _royalties); + if (_royalties > 0) + erc721.setTokenRoyalty(uint256(_did), msg.sender, _royalties); - if (_mint) { + if (_mint) mint721(_did); - } return super.used( keccak256(abi.encode(_did, 1, _royalties, msg.sender)), From a9d38825e0a1c176aca6076cacac5dabb601aa1d Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 18:43:45 +0100 Subject: [PATCH 5/7] wip --- contracts/registry/DIDRegistry.sol | 39 ++++++++++++++------ contracts/token/erc721/NFT721Upgradeable.sol | 7 ++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/contracts/registry/DIDRegistry.sol b/contracts/registry/DIDRegistry.sol index df71aaf1..52a03ca4 100644 --- a/contracts/registry/DIDRegistry.sol +++ b/contracts/registry/DIDRegistry.sol @@ -60,7 +60,7 @@ contract DIDRegistry is DIDFactory { * @param _royalties refers to the royalties to reward to the DID creator in the secondary market * @param _mint if true it mints the ERC-1155 NFTs attached to the asset * @param _activityId refers to activity - * @param _attributes refers to the provenance attributes + * @param _nftMetadata refers to the url providing the NFT Metadata * @return size refers to the size of the registry after the register action. */ function registerMintableDID( @@ -72,13 +72,13 @@ contract DIDRegistry is DIDFactory { uint8 _royalties, bool _mint, bytes32 _activityId, - string memory _attributes + string memory _nftMetadata ) public - onlyValidAttributes(_attributes) + onlyValidAttributes(_nftMetadata) returns (uint size) { - uint result = registerDID(_didSeed, _checksum, _providers, _url, _activityId, _attributes); + uint result = registerDID(_didSeed, _checksum, _providers, _url, _activityId, ''); enableAndMintDidNft( hashDID(_didSeed, msg.sender), _cap, @@ -101,7 +101,7 @@ contract DIDRegistry is DIDFactory { * @param _cap refers to the mint cap * @param _royalties refers to the royalties to reward to the DID creator in the secondary market * @param _activityId refers to activity - * @param _attributes refers to the provenance attributes + * @param _nftMetadata refers to the url providing the NFT Metadata * @return size refers to the size of the registry after the register action. */ function registerMintableDID( @@ -112,19 +112,19 @@ contract DIDRegistry is DIDFactory { uint256 _cap, uint8 _royalties, bytes32 _activityId, - string memory _attributes + string memory _nftMetadata ) public - onlyValidAttributes(_attributes) + onlyValidAttributes(_nftMetadata) returns (uint size) { return registerMintableDID( - _didSeed, _checksum, _providers, _url, _cap, _royalties, false, _activityId, _attributes); + _didSeed, _checksum, _providers, _url, _cap, _royalties, false, _activityId, _nftMetadata); } /** - * @notice enableDidNft creates the initial setup of NFTs minting and royalties distribution. + * @notice enableDidNft creates the initial setup of NFTs minting and royalties distribution for ERC-1155 NFTs. * After this initial setup, this data can't be changed anymore for the DID given, even for the owner of the DID. * The reason of this is to avoid minting additional NFTs after the initial agreement, what could affect the * valuation of NFTs of a DID already created. @@ -134,12 +134,14 @@ contract DIDRegistry is DIDFactory { * @param _cap refers to the mint cap * @param _royalties refers to the royalties to reward to the DID creator in the secondary market * @param _mint if is true mint directly the amount capped tokens and lock in the _lockAddress + * @param _nftMetadata refers to the url providing the NFT Metadata */ function enableAndMintDidNft( bytes32 _did, uint256 _cap, uint8 _royalties, - bool _mint + bool _mint, + string memory _nftMetadata ) public onlyDIDOwner(_did) @@ -157,11 +159,24 @@ contract DIDRegistry is DIDFactory { keccak256(abi.encode(_did, _cap, _royalties, msg.sender)), _did, msg.sender, keccak256('enableNft'), '', 'nft initialization'); } - + + /** + * @notice enableAndMintDidNft721 creates the initial setup of NFTs minting and royalties distribution for ERC-721 NFTs. + * After this initial setup, this data can't be changed anymore for the DID given, even for the owner of the DID. + * The reason of this is to avoid minting additional NFTs after the initial agreement, what could affect the + * valuation of NFTs of a DID already created. + + * @dev update the DID registry providers list by adding the mintCap and royalties configuration + * @param _did refers to decentralized identifier (a byte32 length ID) + * @param _royalties refers to the royalties to reward to the DID creator in the secondary market + * @param _mint if is true mint directly the amount capped tokens and lock in the _lockAddress + * @param _nftMetadata refers to the url providing the NFT Metadata + */ function enableAndMintDidNft721( bytes32 _did, uint8 _royalties, - bool _mint + bool _mint, + string memory _nftMetadata ) public onlyDIDOwner(_did) diff --git a/contracts/token/erc721/NFT721Upgradeable.sol b/contracts/token/erc721/NFT721Upgradeable.sol index 4f594637..c1b0ac44 100644 --- a/contracts/token/erc721/NFT721Upgradeable.sol +++ b/contracts/token/erc721/NFT721Upgradeable.sol @@ -63,6 +63,13 @@ contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, Ac _burn(id); } + function tokenURI(uint256 tokenId) public view override returns (string memory) { + require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; + } + /** * @dev Record the asset royalties * @param tokenId the id of the asset with the royalties associated From bd92d5f488acb22b94eff95bfe38e6ddc811cc1e Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Thu, 13 Jan 2022 19:13:00 +0100 Subject: [PATCH 6/7] NFT721 test was using 1155 tokens --- contracts/registry/DIDRegistry.sol | 47 ++++++++++++++++++++++++++++-- test/int/nft/NFT721_e2e.Test.js | 39 ++++++------------------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/contracts/registry/DIDRegistry.sol b/contracts/registry/DIDRegistry.sol index df71aaf1..6e0450a2 100644 --- a/contracts/registry/DIDRegistry.sol +++ b/contracts/registry/DIDRegistry.sol @@ -47,7 +47,7 @@ contract DIDRegistry is DIDFactory { } /** - * @notice Register a Mintable DID. + * @notice Register a Mintable DID using NFTs based in the ERC-1155 standard. * * @dev The first attribute of a DID registered sets the DID owner. * Subsequent updates record _checksum and update info. @@ -86,8 +86,49 @@ contract DIDRegistry is DIDFactory { _mint ); return result; - } - + } + + /** + * @notice Register a Mintable DID using NFTs based in the ERC-721 standard. + * + * @dev The first attribute of a DID registered sets the DID owner. + * Subsequent updates record _checksum and update info. + * + * @param _didSeed refers to decentralized identifier seed (a bytes32 length ID). + * @param _checksum includes a one-way HASH calculated using the DDO content. + * @param _providers list of addresses that can act as an asset provider + * @param _url refers to the url resolving the DID into a DID Document (DDO), limited to 2048 bytes. + * @param _royalties refers to the royalties to reward to the DID creator in the secondary market + * @param _mint if true it mints the ERC-1155 NFTs attached to the asset + * @param _activityId refers to activity + * @param _attributes refers to the provenance attributes + * @return size refers to the size of the registry after the register action. + */ + function registerMintableDID721( + bytes32 _didSeed, + bytes32 _checksum, + address[] memory _providers, + string memory _url, + uint8 _royalties, + bool _mint, + bytes32 _activityId, + string memory _attributes + ) + public + onlyValidAttributes(_attributes) + returns (uint size) + { + uint result = registerDID(_didSeed, _checksum, _providers, _url, _activityId, _attributes); + enableAndMintDidNft721( + hashDID(_didSeed, msg.sender), + _royalties, + _mint + ); + return result; + } + + + /** * @notice Register a Mintable DID. * diff --git a/test/int/nft/NFT721_e2e.Test.js b/test/int/nft/NFT721_e2e.Test.js index a6092113..4caed696 100644 --- a/test/int/nft/NFT721_e2e.Test.js +++ b/test/int/nft/NFT721_e2e.Test.js @@ -23,8 +23,7 @@ const NeverminedToken = artifacts.require('NeverminedToken') const NFTAccessCondition = artifacts.require('NFTAccessCondition') const NFTHolderCondition = artifacts.require('NFT721HolderCondition') -const TestERC721 = artifacts.require('TestERC721') -const VitaDAOERC721 = artifacts.require('IPNFT') +const TestERC721 = artifacts.require('NFT721Upgradeable') const constants = require('../../helpers/constants.js') const { getBalance } = require('../../helpers/getBalance.js') @@ -32,7 +31,6 @@ const testUtils = require('../../helpers/utils.js') contract('End to End NFT721 Scenarios', (accounts) => { const royalties = 10 // 10% of royalties in the secondary market - const cappedAmount = 5 const didSeed = testUtils.generateId() let did const agreementId = testUtils.generateId() @@ -92,8 +90,13 @@ contract('End to End NFT721 Scenarios', (accounts) => { token = await NeverminedToken.new() await token.initialize(owner, owner) + nft = await TestERC721.new({ from: deployer }) + await nft.initialize({ from: owner }) + didRegistry = await DIDRegistry.new() - await didRegistry.initialize(owner, constants.address.zero, constants.address.zero) + await didRegistry.initialize(owner, constants.address.zero, nft.address) + + await nft.addMinter(didRegistry.address) conditionStoreManager = await ConditionStoreManager.new() @@ -491,37 +494,13 @@ contract('End to End NFT721 Scenarios', (accounts) => { describe('Test NFT721', () => { describe('As an artist I want to register a new artwork', () => { it('I want to register a new artwork and tokenize (via NFT). I want to get 10% of royalties', async () => { - nft = await TestERC721.new({ from: deployer }) - await nft.initialize({ from: owner }) - - const { didRegistry } = await setupTest() - - did = await didRegistry.hashDID(didSeed, artist) - - await didRegistry.registerMintableDID( - didSeed, checksum, [], url, cappedAmount, royalties, constants.activities.GENERATED, '', { from: artist }) - - await nft.mint(did, { from: artist }) - await nft.setApprovalForAll(transferCondition.address, true, { from: artist }) - }) - }) - - runTests() - }) - - describe('VitaDAO NFT721', () => { - describe('As an artist I want to register a new artwork', () => { - it('I want to register a new artwork and tokenize (via NFT). I want to get 10% of royalties', async () => { - nft = await VitaDAOERC721.new({ from: deployer }) - await nft.initialize('VitaNFT', 'VitaNFT', { from: owner }) const { didRegistry } = await setupTest() did = await didRegistry.hashDID(didSeed, artist) - await didRegistry.registerMintableDID( - didSeed, checksum, [], url, cappedAmount, royalties, constants.activities.GENERATED, '', { from: artist }) + await didRegistry.registerMintableDID721( + didSeed, checksum, [], url, royalties, true, constants.activities.GENERATED, '', { from: artist }) - await nft.mint(artist, did, url, { from: owner }) await nft.setApprovalForAll(transferCondition.address, true, { from: artist }) }) }) From 0d49fbf7c2fd6fa53d2406840f5d9c6c205f9ac8 Mon Sep 17 00:00:00 2001 From: Aitor <1726644+aaitor@users.noreply.github.com> Date: Fri, 14 Jan 2022 14:37:01 +0100 Subject: [PATCH 7/7] Adding support to NFTs metadata (ERC-721 & 1155) --- contracts/registry/DIDRegistry.sol | 20 +++++--- .../{erc2981/ERC2981.sol => NFTBase.sol} | 51 +++++++++++++++---- contracts/token/erc1155/NFTUpgradeable.sol | 47 +++++++++-------- contracts/token/erc721/NFT721Upgradeable.sol | 50 ++++++++---------- test/unit/registry/Mintable721DIDRegistry.js | 41 ++++++++++----- test/unit/registry/MintableDIDRegistry.js | 12 +++-- 6 files changed, 137 insertions(+), 84 deletions(-) rename contracts/token/{erc2981/ERC2981.sol => NFTBase.sol} (51%) diff --git a/contracts/registry/DIDRegistry.sol b/contracts/registry/DIDRegistry.sol index 8a4e065f..322f2c8d 100644 --- a/contracts/registry/DIDRegistry.sol +++ b/contracts/registry/DIDRegistry.sol @@ -83,7 +83,8 @@ contract DIDRegistry is DIDFactory { hashDID(_didSeed, msg.sender), _cap, _royalties, - _mint + _mint, + _nftMetadata ); return result; } @@ -101,7 +102,7 @@ contract DIDRegistry is DIDFactory { * @param _royalties refers to the royalties to reward to the DID creator in the secondary market * @param _mint if true it mints the ERC-1155 NFTs attached to the asset * @param _activityId refers to activity - * @param _attributes refers to the provenance attributes + * @param _nftMetadata refers to the url providing the NFT Metadata * @return size refers to the size of the registry after the register action. */ function registerMintableDID721( @@ -112,17 +113,18 @@ contract DIDRegistry is DIDFactory { uint8 _royalties, bool _mint, bytes32 _activityId, - string memory _attributes + string memory _nftMetadata ) public - onlyValidAttributes(_attributes) + onlyValidAttributes(_nftMetadata) returns (uint size) { - uint result = registerDID(_didSeed, _checksum, _providers, _url, _activityId, _attributes); + uint result = registerDID(_didSeed, _checksum, _providers, _url, _activityId, ''); enableAndMintDidNft721( hashDID(_didSeed, msg.sender), _royalties, - _mint + _mint, + _nftMetadata ); return result; } @@ -190,6 +192,9 @@ contract DIDRegistry is DIDFactory { { didRegisterList.initializeNftConfig(_did, _cap, _royalties); + if (bytes(_nftMetadata).length > 0) + erc1155.setNFTMetadata(uint256(_did), _nftMetadata); + if (_royalties > 0) erc1155.setTokenRoyalty(uint256(_did), msg.sender, _royalties); @@ -225,6 +230,9 @@ contract DIDRegistry is DIDFactory { { didRegisterList.initializeNft721Config(_did, _royalties); + if (bytes(_nftMetadata).length > 0) + erc721.setNFTMetadata(uint256(_did), _nftMetadata); + if (_royalties > 0) erc721.setTokenRoyalty(uint256(_did), msg.sender, _royalties); diff --git a/contracts/token/erc2981/ERC2981.sol b/contracts/token/NFTBase.sol similarity index 51% rename from contracts/token/erc2981/ERC2981.sol rename to contracts/token/NFTBase.sol index 01946f3a..7017c610 100644 --- a/contracts/token/erc2981/ERC2981.sol +++ b/contracts/token/NFTBase.sol @@ -4,34 +4,63 @@ pragma solidity ^0.8.0; // Code is Apache-2.0 and docs are CC-BY-4.0 import '@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol'; /** * * @dev Implementation of the Royalties EIP-2981 base contract * See https://eips.ethereum.org/EIPS/eip-2981 */ -abstract contract ERC2981 is IERC2981Upgradeable { +abstract contract NFTBase is IERC2981Upgradeable, OwnableUpgradeable, AccessControlUpgradeable { + // Mapping from account to proxy approvals + mapping (address => bool) internal _proxyApprovals; + + bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); + struct RoyaltyInfo { address receiver; uint256 royaltyAmount; } + struct NFTMetadata { + string nftURI; + } + // Mapping of Royalties per tokenId (DID) mapping(uint256 => RoyaltyInfo) internal _royalties; - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override - returns (bool) + mapping(uint256 => NFTMetadata) internal _metadata; + + /** + * Event for recording proxy approvals. + */ + event ProxyApproval(address sender, address operator, bool approved); + + + function setProxyApproval( + address operator, + bool approved + ) + public + onlyOwner + virtual { - return - interfaceId == type(IERC2981Upgradeable).interfaceId || - supportsInterface(interfaceId); + _proxyApprovals[operator] = approved; + emit ProxyApproval(_msgSender(), operator, approved); } - + + function _setNFTMetadata( + uint256 tokenId, + string memory tokenURI + ) + internal + { + _metadata[tokenId] = NFTMetadata(tokenURI); + } + function _setTokenRoyalty( uint256 tokenId, address receiver, diff --git a/contracts/token/erc1155/NFTUpgradeable.sol b/contracts/token/erc1155/NFTUpgradeable.sol index 002781fd..7c71660f 100644 --- a/contracts/token/erc1155/NFTUpgradeable.sol +++ b/contracts/token/erc1155/NFTUpgradeable.sol @@ -3,27 +3,15 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; -import '../erc2981/ERC2981.sol'; +import '../NFTBase.sol'; /** * * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 */ -contract NFTUpgradeable is ERC1155Upgradeable, ERC2981, OwnableUpgradeable, AccessControlUpgradeable { - - // Mapping from account to proxy approvals - mapping (address => bool) private _proxyApprovals; - - bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); +contract NFTUpgradeable is ERC1155Upgradeable, NFTBase { - /** - * Event for recording proxy approvals. - */ - event ProxyApproval(address sender, address operator, bool approved); - /** * @dev See {_setURI}. */ @@ -37,11 +25,6 @@ contract NFTUpgradeable is ERC1155Upgradeable, ERC2981, OwnableUpgradeable, Acce AccessControlUpgradeable._setupRole(MINTER_ROLE, msg.sender); } - function setProxyApproval(address operator, bool approved) public onlyOwner virtual { - _proxyApprovals[operator] = approved; - emit ProxyApproval(_msgSender(), operator, approved); - } - /** * @dev See {IERC1155-isApprovedForAll}. */ @@ -63,6 +46,26 @@ contract NFTUpgradeable is ERC1155Upgradeable, ERC2981, OwnableUpgradeable, Acce AccessControlUpgradeable._setupRole(MINTER_ROLE, account); } +// function uri(uint256 id) external view returns (string memory) + function uri(uint256 tokenId) public view override returns (string memory) { + return _metadata[tokenId].nftURI; + } + + /** + * @dev Record some NFT Metadata + * @param tokenId the id of the asset with the royalties associated + * @param nftURI the URI (https, ipfs, etc) to the metadata describing the NFT + */ + function setNFTMetadata( + uint256 tokenId, + string memory nftURI + ) + public + { + require(hasRole(MINTER_ROLE, msg.sender), 'only minter'); + _setNFTMetadata(tokenId, nftURI); + } + /** * @dev Record the asset royalties * @param tokenId the id of the asset with the royalties associated @@ -86,12 +89,12 @@ contract NFTUpgradeable is ERC1155Upgradeable, ERC2981, OwnableUpgradeable, Acce public view virtual - override(AccessControlUpgradeable, ERC1155Upgradeable, ERC2981) + override(ERC1155Upgradeable, IERC165Upgradeable) returns (bool) { return AccessControlUpgradeable.supportsInterface(interfaceId) - || ERC1155Upgradeable.supportsInterface(interfaceId) - || ERC2981.supportsInterface(interfaceId); + || ERC1155Upgradeable.supportsInterface(interfaceId) + || interfaceId == type(IERC2981Upgradeable).interfaceId; } } diff --git a/contracts/token/erc721/NFT721Upgradeable.sol b/contracts/token/erc721/NFT721Upgradeable.sol index c1b0ac44..4e26297f 100644 --- a/contracts/token/erc721/NFT721Upgradeable.sol +++ b/contracts/token/erc721/NFT721Upgradeable.sol @@ -3,25 +3,13 @@ pragma solidity ^0.8.0; import '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; -import '../erc2981/ERC2981.sol'; +import '../NFTBase.sol'; /** * * @dev Implementation of the basic standard multi-token. */ -contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, AccessControlUpgradeable { - - // Mapping from account to proxy approvals - mapping (address => bool) private _proxyApprovals; - - bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); - - /** - * Event for recording proxy approvals. - */ - event ProxyApproval(address sender, address operator, bool approved); +contract NFT721Upgradeable is ERC721Upgradeable, NFTBase { /** * @dev See {_setURI}. @@ -35,13 +23,7 @@ contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, Ac AccessControlUpgradeable.__AccessControl_init(); AccessControlUpgradeable._setupRole(MINTER_ROLE, msg.sender); } - - function setProxyApproval(address operator, bool approved) public onlyOwner virtual { - _proxyApprovals[operator] = approved; - emit ProxyApproval(_msgSender(), operator, approved); - } - /** * @dev See {IERC1155-isApprovedForAll}. */ @@ -62,14 +44,26 @@ contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, Ac require(hasRole(MINTER_ROLE, msg.sender), 'only minter can burn'); _burn(id); } - + function tokenURI(uint256 tokenId) public view override returns (string memory) { - require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); + return _metadata[tokenId].nftURI; + } + + /** + * @dev Record some NFT Metadata + * @param tokenId the id of the asset with the royalties associated + * @param nftURI the URI (https, ipfs, etc) to the metadata describing the NFT + */ + function setNFTMetadata( + uint256 tokenId, + string memory nftURI + ) + public + { + require(hasRole(MINTER_ROLE, msg.sender), 'only minter'); + _setNFTMetadata(tokenId, nftURI); + } - string memory baseURI = _baseURI(); - return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; - } - /** * @dev Record the asset royalties * @param tokenId the id of the asset with the royalties associated @@ -93,12 +87,12 @@ contract NFT721Upgradeable is ERC721Upgradeable, ERC2981, OwnableUpgradeable, Ac public view virtual - override(AccessControlUpgradeable, ERC721Upgradeable, ERC2981) + override(ERC721Upgradeable, IERC165Upgradeable) returns (bool) { return AccessControlUpgradeable.supportsInterface(interfaceId) || ERC721Upgradeable.supportsInterface(interfaceId) - || ERC2981.supportsInterface(interfaceId); + || interfaceId == type(IERC2981Upgradeable).interfaceId; } } diff --git a/test/unit/registry/Mintable721DIDRegistry.js b/test/unit/registry/Mintable721DIDRegistry.js index a60a49ef..71aeb639 100644 --- a/test/unit/registry/Mintable721DIDRegistry.js +++ b/test/unit/registry/Mintable721DIDRegistry.js @@ -17,6 +17,7 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { const other = accounts[2] const consumer = accounts[3] const value = 'https://nevermined.io/did/nevermined/test-attr-example.txt' + const nftMetadataURL = 'https://nevermined.io/metadata.json' let didRegistry let didRegistryLibrary let didRegistryLibraryProxy @@ -100,18 +101,32 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft721(did, 0, true, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, true, nftMetadataURL, { from: owner }) const nftOwner = await nft.ownerOf(did) assert.strictEqual(owner, nftOwner) - await didRegistry.burn721(did, - { - from: owner - } - ) - + await didRegistry.burn721(did, { from: owner }) await assert.isRejected(nft.ownerOf(did)) + + const _nftURI = await nft.tokenURI(did) + assert.strictEqual(nftMetadataURL, _nftURI) + }) + + it('Should work with an empty NFT Metadata URL', async () => { + const didSeed = testUtils.generateId() + const did = await didRegistry.hashDID(didSeed, owner) + const checksum = testUtils.generateId() + + await didRegistry.registerAttribute( + didSeed, checksum, [], value, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, true, '', { from: owner }) + + const nftOwner = await nft.ownerOf(did) + assert.strictEqual(owner, nftOwner) + + const _nftURI = await nft.tokenURI(did) + assert.strictEqual('', _nftURI) }) it('The royalties should be initialized and retrieved (ERC-2981)', async () => { @@ -121,7 +136,7 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft721(did, 10, true, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 10, true, nftMetadataURL, { from: owner }) const nftOwner = await nft.ownerOf(did) assert.strictEqual(owner, nftOwner) @@ -138,7 +153,7 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft721(did, 0, true, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, true, nftMetadataURL, { from: owner }) const nftOwner = await nft.ownerOf(did) assert.strictEqual(owner, nftOwner) @@ -151,7 +166,7 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft721(did, 0, false, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, false, nftMetadataURL, { from: owner }) await assert.isRejected(nft.ownerOf(did)) @@ -167,7 +182,7 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { const checksum = testUtils.generateId() await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft721(did, 0, false, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, false, nftMetadataURL, { from: owner }) await didRegistry.mint721(did, { from: owner }) let nftOwner = await nft.ownerOf(did) @@ -190,11 +205,11 @@ contract('Mintable DIDRegistry (ERC-721)', (accounts) => { await assert.isRejected( // Must not allow to initialize NFTs if not the owner - didRegistry.enableAndMintDidNft721(did, 0, true, { from: other }), + didRegistry.enableAndMintDidNft721(did, 0, true, nftMetadataURL, { from: other }), 'Only owner' ) - await didRegistry.enableAndMintDidNft721(did, 0, true, { from: owner }) + await didRegistry.enableAndMintDidNft721(did, 0, true, nftMetadataURL, { from: owner }) await assert.isRejected( // Must not allow to mint tokens without previous initialization didRegistry.mint721(did, { from: other }), diff --git a/test/unit/registry/MintableDIDRegistry.js b/test/unit/registry/MintableDIDRegistry.js index b12a6328..1ef068ee 100644 --- a/test/unit/registry/MintableDIDRegistry.js +++ b/test/unit/registry/MintableDIDRegistry.js @@ -17,6 +17,7 @@ contract('Mintable DIDRegistry', (accounts) => { const other = accounts[2] const consumer = accounts[3] const value = 'https://nevermined.io/did/nevermined/test-attr-example.txt' + const nftMetadataURL = 'https://nevermined.io/metadata.json' let didRegistry let didRegistryLibrary let didRegistryLibraryProxy @@ -100,7 +101,7 @@ contract('Mintable DIDRegistry', (accounts) => { const checksum = testUtils.generateId() await didRegistry.registerMintableDID( - didSeed, checksum, [], value, 20, 0, constants.activities.GENERATED, '', { from: owner }) + didSeed, checksum, [], value, 20, 0, constants.activities.GENERATED, nftMetadataURL, { from: owner }) await didRegistry.mint(did, 20, { from: owner }) let balance = await nft.balanceOf(owner, did) @@ -114,6 +115,9 @@ contract('Mintable DIDRegistry', (accounts) => { balance = await nft.balanceOf(owner, did) assert.strictEqual(15, balance.toNumber()) + + const _nftURI = await nft.uri(did) + assert.strictEqual(nftMetadataURL, _nftURI) }) it('Should initialize the NFT in the registration', async () => { @@ -147,7 +151,7 @@ contract('Mintable DIDRegistry', (accounts) => { await didRegistry.registerAttribute( didSeed, checksum, [], value, { from: owner }) - await didRegistry.enableAndMintDidNft(did, 5, 0, true, { from: owner }) + await didRegistry.enableAndMintDidNft(did, 5, 0, true, nftMetadataURL, { from: owner }) const balanceOwner = await nft.balanceOf(owner, did) assert.strictEqual(5, balanceOwner.toNumber()) @@ -200,11 +204,11 @@ contract('Mintable DIDRegistry', (accounts) => { await assert.isRejected( // Must not allow to initialize NFTs if not the owner - didRegistry.enableAndMintDidNft(did, 5, 0, true, { from: other }), + didRegistry.enableAndMintDidNft(did, 5, 0, true, nftMetadataURL, { from: other }), 'Only owner' ) - await didRegistry.enableAndMintDidNft(did, 5, 0, true, { from: owner }) + await didRegistry.enableAndMintDidNft(did, 5, 0, true, nftMetadataURL, { from: owner }) await assert.isRejected( // Must not allow to mint tokens without previous initialization didRegistry.mint(did, 1, { from: other }),