From 6421bfed3e87a1b120857731f4ac0712072cb575 Mon Sep 17 00:00:00 2001 From: Tomi_Ohl Date: Thu, 29 Jun 2023 19:56:17 +0200 Subject: [PATCH] Implement EIP-4906 (MetadataUpdated event) --- contracts/GuildPin.sol | 2 +- contracts/interfaces/IERC4906.sol | 18 +++++++++++ contracts/interfaces/IGuildPin.sol | 4 --- contracts/token/SoulboundERC721.sol | 11 +++++-- docs/contracts/interfaces/IERC4906.md | 43 ++++++++++++++++++++++++++ docs/contracts/interfaces/IGuildPin.md | 15 --------- test/GuildPin.spec.ts | 4 +-- 7 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 contracts/interfaces/IERC4906.sol create mode 100644 docs/contracts/interfaces/IERC4906.md diff --git a/contracts/GuildPin.sol b/contracts/GuildPin.sol index 650beac..bb77c35 100644 --- a/contracts/GuildPin.sol +++ b/contracts/GuildPin.sol @@ -141,7 +141,7 @@ contract GuildPin is IGuildPin, Initializable, OwnableUpgradeable, UUPSUpgradeab cids[tokenId] = newCid; - emit TokenURIUpdated(tokenId); + emit MetadataUpdate(tokenId); } function setPinStrings(GuildAction guildAction, PinStrings memory pinStrings) public onlyOwner { diff --git a/contracts/interfaces/IERC4906.sol b/contracts/interfaces/IERC4906.sol new file mode 100644 index 0000000..395f8e9 --- /dev/null +++ b/contracts/interfaces/IERC4906.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; +import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; + +/// @title EIP-721 Metadata Update Extension +interface IERC4906 is IERC165Upgradeable, IERC721Upgradeable { + /// @dev This event emits when the metadata of a token is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFT. + event MetadataUpdate(uint256 _tokenId); + + /// @dev This event emits when the metadata of a range of tokens is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFTs. + event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); +} diff --git a/contracts/interfaces/IGuildPin.sol b/contracts/interfaces/IGuildPin.sol index fabd802..1b5655b 100644 --- a/contracts/interfaces/IGuildPin.sol +++ b/contracts/interfaces/IGuildPin.sol @@ -106,10 +106,6 @@ interface IGuildPin { /// @param guildAction The action whose strings were set. event PinStringsSet(GuildAction guildAction); - /// @notice Event emitted whenever a token's cid is updated. - /// @param tokenId The id of the updated token. - event TokenURIUpdated(uint256 tokenId); - /// @notice Event emitted when the validSigner is changed. /// @param newValidSigner The new address of validSigner. event ValidSignerChanged(address newValidSigner); diff --git a/contracts/token/SoulboundERC721.sol b/contracts/token/SoulboundERC721.sol index d6b0a1e..652a5b7 100644 --- a/contracts/token/SoulboundERC721.sol +++ b/contracts/token/SoulboundERC721.sol @@ -3,16 +3,18 @@ pragma solidity 0.8.19; /* solhint-disable max-line-length */ +import { IERC4906 } from "../interfaces/IERC4906.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import { ERC721EnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import { IERC721EnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; +import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; /* solhint-enable max-line-length */ /// @title An enumerable soulbound ERC721. /// @notice Allowance and transfer-related functions are disabled. -contract SoulboundERC721 is ERC721Upgradeable, ERC721EnumerableUpgradeable { +contract SoulboundERC721 is ERC721Upgradeable, ERC721EnumerableUpgradeable, IERC4906 { /// @notice Empty space reserved for future updates. uint256[50] private __gap; @@ -28,8 +30,11 @@ contract SoulboundERC721 is ERC721Upgradeable, ERC721EnumerableUpgradeable { /// @inheritdoc ERC721EnumerableUpgradeable function supportsInterface( bytes4 interfaceId - ) public view virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable) returns (bool) { - return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId); + ) public view virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable, IERC165Upgradeable) returns (bool) { + return + interfaceId == 0x49064906 || // ERC4906 + interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || + super.supportsInterface(interfaceId); } function approve( diff --git a/docs/contracts/interfaces/IERC4906.md b/docs/contracts/interfaces/IERC4906.md new file mode 100644 index 0000000..f6aeef3 --- /dev/null +++ b/docs/contracts/interfaces/IERC4906.md @@ -0,0 +1,43 @@ +# IERC4906 + +EIP-721 Metadata Update Extension + +## Events + +### MetadataUpdate + +```solidity +event MetadataUpdate( + uint256 _tokenId +) +``` + +This event emits when the metadata of a token is changed. +So that the third-party platforms such as NFT market could +timely update the images and related attributes of the NFT. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :---------- | +| `_tokenId` | uint256 | | +### BatchMetadataUpdate + +```solidity +event BatchMetadataUpdate( + uint256 _fromTokenId, + uint256 _toTokenId +) +``` + +This event emits when the metadata of a range of tokens is changed. +So that the third-party platforms such as NFT market could +timely update the images and related attributes of the NFTs. + +#### Parameters + +| Name | Type | Description | +| :--- | :--- | :---------- | +| `_fromTokenId` | uint256 | | +| `_toTokenId` | uint256 | | + diff --git a/docs/contracts/interfaces/IGuildPin.md b/docs/contracts/interfaces/IGuildPin.md index 01bff33..fa519fa 100644 --- a/docs/contracts/interfaces/IGuildPin.md +++ b/docs/contracts/interfaces/IGuildPin.md @@ -177,21 +177,6 @@ Event emitted when pretty strings are set for a GuildAction. | Name | Type | Description | | :--- | :--- | :---------- | | `guildAction` | enum IGuildPin.GuildAction | The action whose strings were set. | -### TokenURIUpdated - -```solidity -event TokenURIUpdated( - uint256 tokenId -) -``` - -Event emitted whenever a token's cid is updated. - -#### Parameters - -| Name | Type | Description | -| :--- | :--- | :---------- | -| `tokenId` | uint256 | The id of the updated token. | ### ValidSignerChanged ```solidity diff --git a/test/GuildPin.spec.ts b/test/GuildPin.spec.ts index b556fd4..f149389 100644 --- a/test/GuildPin.spec.ts +++ b/test/GuildPin.spec.ts @@ -705,9 +705,9 @@ describe("GuildPin", () => { expect(decodeTokenURI(newTokenURI)).to.contain(cids[1]); }); - it("should emit TokenURIUpdated event", async () => { + it("should emit MetadataUpdate event", async () => { await expect(pin.updateImageURI(samplePinData, timestamp, cids[0], signature)) - .to.emit(pin, "TokenURIUpdated") + .to.emit(pin, "MetadataUpdate") .withArgs(1); }); });