diff --git a/src/extension/token/metadata/SimpleMetadataERC1155.sol b/src/extension/token/metadata/SimpleMetadataERC1155.sol index 436a5eff..4106b261 100644 --- a/src/extension/token/metadata/SimpleMetadataERC1155.sol +++ b/src/extension/token/metadata/SimpleMetadataERC1155.sol @@ -10,7 +10,7 @@ contract SimpleMetadataERC1155 is SimpleMetadataERC721 { config.callbackFunctions = new CallbackFunction[](1); config.fallbackFunctions = new FallbackFunction[](1); - config.callbackFunctions[0] = CallbackFunction(this.onTokenURI.selector, CallType.CALL); + config.callbackFunctions[0] = CallbackFunction(this.onTokenURI.selector, CallType.STATICCALL); config.fallbackFunctions[0] = FallbackFunction({ selector: this.setTokenURI.selector, callType: CallType.CALL, diff --git a/src/extension/token/metadata/SimpleMetadataERC721.sol b/src/extension/token/metadata/SimpleMetadataERC721.sol index 4a9e6c61..d9e15d75 100644 --- a/src/extension/token/metadata/SimpleMetadataERC721.sol +++ b/src/extension/token/metadata/SimpleMetadataERC721.sol @@ -42,7 +42,7 @@ contract SimpleMetadataERC721 is ModularExtension { config.callbackFunctions = new CallbackFunction[](1); config.fallbackFunctions = new FallbackFunction[](1); - config.callbackFunctions[0] = CallbackFunction(this.onTokenURI.selector, CallType.CALL); + config.callbackFunctions[0] = CallbackFunction(this.onTokenURI.selector, CallType.STATICCALL); config.fallbackFunctions[0] = FallbackFunction({ selector: this.setTokenURI.selector, callType: CallType.CALL, diff --git a/test/extension/BatchMetadataERC1155.t.sol b/test/extension/metadata/BatchMetadataERC1155.t.sol similarity index 100% rename from test/extension/BatchMetadataERC1155.t.sol rename to test/extension/metadata/BatchMetadataERC1155.t.sol diff --git a/test/extension/BatchMetadataERC721.t.sol b/test/extension/metadata/BatchMetadataERC721.t.sol similarity index 100% rename from test/extension/BatchMetadataERC721.t.sol rename to test/extension/metadata/BatchMetadataERC721.t.sol diff --git a/test/extension/DelayedRevealBatchMetadataERC721.t.sol b/test/extension/metadata/DelayedRevealBatchMetadataERC721.t.sol similarity index 100% rename from test/extension/DelayedRevealBatchMetadataERC721.t.sol rename to test/extension/metadata/DelayedRevealBatchMetadataERC721.t.sol diff --git a/test/extension/OpenEditionMetadataERC1155.t.sol b/test/extension/metadata/OpenEditionMetadataERC1155.t.sol similarity index 100% rename from test/extension/OpenEditionMetadataERC1155.t.sol rename to test/extension/metadata/OpenEditionMetadataERC1155.t.sol diff --git a/test/extension/OpenEditionMetadataERC721.t.sol b/test/extension/metadata/OpenEditionMetadataERC721.t.sol similarity index 100% rename from test/extension/OpenEditionMetadataERC721.t.sol rename to test/extension/metadata/OpenEditionMetadataERC721.t.sol diff --git a/test/extension/metadata/SimpleMetadataERC1155.t.sol b/test/extension/metadata/SimpleMetadataERC1155.t.sol new file mode 100644 index 00000000..99149845 --- /dev/null +++ b/test/extension/metadata/SimpleMetadataERC1155.t.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "lib/forge-std/src/console.sol"; + +import {Test} from "forge-std/Test.sol"; +import {ERC1967Factory} from "@solady/utils/ERC1967Factory.sol"; +import {ERC1967FactoryConstants} from "@solady/utils/ERC1967FactoryConstants.sol"; + +// Target contract +import {IExtensionConfig} from "src/interface/IExtensionConfig.sol"; +import {IModularCore} from "src/interface/IModularCore.sol"; +import {ModularExtension} from "src/ModularExtension.sol"; +import {ModularCoreUpgradeable} from "src/ModularCoreUpgradeable.sol"; +import {ERC1155Core} from "src/core/token/ERC1155Core.sol"; +import {SimpleMetadataERC1155} from "src/extension/token/metadata/SimpleMetadataERC1155.sol"; +import {SimpleMetadataERC721, SimpleMetadataStorage} from "src/extension/token/metadata/SimpleMetadataERC721.sol"; + +contract SimpleMetadataExt is SimpleMetadataERC1155 { + function uris(address token, uint256 id) external view returns (string memory) { + return SimpleMetadataStorage.data().uris[token][id]; + } +} + +contract SimpleMetadataERC1155Test is Test { + ERC1155Core public core; + + SimpleMetadataExt public extensionImplementation; + SimpleMetadataExt public installedExtension; + + address public owner = address(0x1); + address public permissionedActor = address(0x2); + address public unpermissionedActor = address(0x3); + + function setUp() public { + // Deterministic, canonical ERC1967Factory contract + vm.etch(ERC1967FactoryConstants.ADDRESS, ERC1967FactoryConstants.BYTECODE); + + address[] memory extensions; + bytes[] memory extensionData; + + core = new ERC1155Core(ERC1967FactoryConstants.ADDRESS, "test", "TEST", "", owner, extensions, extensionData); + extensionImplementation = new SimpleMetadataExt(); + + // install extension + vm.prank(owner); + core.installExtension(address(extensionImplementation), ""); + + IModularCore.InstalledExtension[] memory installedExtensions = core.getInstalledExtensions(); + installedExtension = SimpleMetadataExt(installedExtensions[0].implementation); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: `setTokenURI` + //////////////////////////////////////////////////////////////*/ + + function test_state_setTokenURI() public { + vm.prank(owner); + SimpleMetadataExt(address(core)).setTokenURI(1, "ipfs://base/1"); + + vm.prank(owner); + SimpleMetadataExt(address(core)).setTokenURI(2, "ipfs://base/2"); + + // read state from core + assertEq(core.uri(1), "ipfs://base/1"); + assertEq(core.uri(2), "ipfs://base/2"); + assertEq(core.uri(3), ""); + + // read state from the installed extension + assertEq(installedExtension.uris(address(core), 1), "ipfs://base/1"); + assertEq(installedExtension.uris(address(core), 2), "ipfs://base/2"); + assertEq(installedExtension.uris(address(core), 3), ""); + } + + function test_revert_setTokenURI() public { + vm.expectRevert(0x82b42900); // `Unauthorized()` + SimpleMetadataExt(address(core)).setTokenURI(1, "ipfs://base/"); + } +} diff --git a/test/extension/metadata/SimpleMetadataERC721.t.sol b/test/extension/metadata/SimpleMetadataERC721.t.sol new file mode 100644 index 00000000..1f647ffb --- /dev/null +++ b/test/extension/metadata/SimpleMetadataERC721.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "lib/forge-std/src/console.sol"; + +import {Test} from "forge-std/Test.sol"; +import {ERC1967Factory} from "@solady/utils/ERC1967Factory.sol"; +import {ERC1967FactoryConstants} from "@solady/utils/ERC1967FactoryConstants.sol"; + +// Target contract +import {IExtensionConfig} from "src/interface/IExtensionConfig.sol"; +import {IModularCore} from "src/interface/IModularCore.sol"; +import {ModularExtension} from "src/ModularExtension.sol"; +import {ModularCoreUpgradeable} from "src/ModularCoreUpgradeable.sol"; +import {ERC721Core} from "src/core/token/ERC721Core.sol"; +import {SimpleMetadataERC721, SimpleMetadataStorage} from "src/extension/token/metadata/SimpleMetadataERC721.sol"; + +contract SimpleMetadataExt is SimpleMetadataERC721 { + function uris(address token, uint256 id) external view returns (string memory) { + return SimpleMetadataStorage.data().uris[token][id]; + } +} + +contract SimpleMetadataERC721Test is Test { + ERC721Core public core; + + SimpleMetadataExt public extensionImplementation; + SimpleMetadataExt public installedExtension; + + address public owner = address(0x1); + address public permissionedActor = address(0x2); + address public unpermissionedActor = address(0x3); + + function setUp() public { + // Deterministic, canonical ERC1967Factory contract + vm.etch(ERC1967FactoryConstants.ADDRESS, ERC1967FactoryConstants.BYTECODE); + + address[] memory extensions; + bytes[] memory extensionData; + + core = new ERC721Core(ERC1967FactoryConstants.ADDRESS, "test", "TEST", "", owner, extensions, extensionData); + extensionImplementation = new SimpleMetadataExt(); + + // install extension + vm.prank(owner); + core.installExtension(address(extensionImplementation), ""); + + IModularCore.InstalledExtension[] memory installedExtensions = core.getInstalledExtensions(); + installedExtension = SimpleMetadataExt(installedExtensions[0].implementation); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: `setTokenURI` + //////////////////////////////////////////////////////////////*/ + + function test_state_setTokenURI() public { + vm.prank(owner); + SimpleMetadataExt(address(core)).setTokenURI(1, "ipfs://base/1"); + + vm.prank(owner); + SimpleMetadataExt(address(core)).setTokenURI(2, "ipfs://base/2"); + + // read state from core + assertEq(core.tokenURI(1), "ipfs://base/1"); + assertEq(core.tokenURI(2), "ipfs://base/2"); + assertEq(core.tokenURI(3), ""); + + // read state from the installed extension + assertEq(installedExtension.uris(address(core), 1), "ipfs://base/1"); + assertEq(installedExtension.uris(address(core), 2), "ipfs://base/2"); + assertEq(installedExtension.uris(address(core), 3), ""); + } + + function test_revert_setTokenURI() public { + vm.expectRevert(0x82b42900); // `Unauthorized()` + SimpleMetadataExt(address(core)).setTokenURI(1, "ipfs://base/"); + } +}