diff --git a/contracts/registries/metadata/IPAssetRenderer.sol b/contracts/registries/metadata/IPAssetRenderer.sol deleted file mode 100644 index d602ce1a..00000000 --- a/contracts/registries/metadata/IPAssetRenderer.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.23; - -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; -import { IP } from "../../lib/IP.sol"; -import { IIPAccount } from "../../interfaces/IIPAccount.sol"; -import { IPAssetRegistry } from "../../registries/IPAssetRegistry.sol"; -import { IMetadataProvider } from "../../interfaces/registries/metadata/IMetadataProvider.sol"; -import { LicenseRegistry } from "../../registries/LicenseRegistry.sol"; -import { RoyaltyModule } from "../../modules/royalty/RoyaltyModule.sol"; - -/// @title IP Asset Renderer -/// @notice The IP asset renderer is responsible for rendering canonical -/// metadata associated with each IP asset. This includes generation -/// of attributes, on-chain SVGs, and external URLs. Note that the -/// underlying data being rendered is strictly immutable. -contract IPAssetRenderer { - /// @notice The global IP asset registry. - IPAssetRegistry public immutable IP_ASSET_REGISTRY; - - /// @notice The global licensing registry. - LicenseRegistry public immutable LICENSE_REGISTRY; - - // Modules storing attribution related to IPs. - RoyaltyModule public immutable ROYALTY_MODULE; - - /// @notice Initializes the IP asset renderer. - /// TODO: Add different customization options - e.g. font, colorways, etc. - /// TODO: Add an external URL for generating SP-branded links for each IP. - constructor(address assetRegistry, address licenseRegistry, address royaltyModule) { - IP_ASSET_REGISTRY = IPAssetRegistry(assetRegistry); - LICENSE_REGISTRY = LicenseRegistry(licenseRegistry); - ROYALTY_MODULE = RoyaltyModule(royaltyModule); - } - - // TODO: Add contract URI support for metadata about the entire IP registry. - - // TODO: Add rendering functions around licensing information. - - // TODO: Add rendering functions around royalties information. - - // TODO: Add rendering functions around tagging information. - - /// @notice Fetches the canonical name associated with the specified IP. - /// @param ipId The canonical ID of the specified IP. - function name(address ipId) external view returns (string memory) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return metadata.name; - } - - /// @notice Fetches the canonical description associated with the IP. - /// @param ipId The canonical ID of the specified IP. - /// @return The string descriptor of the IP. - /// TODO: Add more information related to licensing or royalties. - /// TODO: Update the description to an SP base URL if external URL not set. - function description(address ipId) public view returns (string memory) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return - string.concat( - metadata.name, - ", IP #", - Strings.toHexString(ipId), - ", is currently owned by", - Strings.toHexString(owner(ipId)), - ". To learn more about this IP, visit ", - metadata.uri - ); - } - - /// @notice Fetches the keccak-256 content hash associated with the specified IP. - /// @param ipId The canonical ID of the specified IP. - /// @return The bytes32 content hash of the IP. - function hash(address ipId) external view returns (bytes32) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return metadata.hash; - } - - /// @notice Fetches the date of registration of the IP. - /// @param ipId The canonical ID of the specified IP. - function registrationDate(address ipId) external view returns (uint64) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return metadata.registrationDate; - } - - /// @notice Fetches the initial registrant of the IP. - /// @param ipId The canonical ID of the specified IP. - function registrant(address ipId) external view returns (address) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return metadata.registrant; - } - - /// @notice Fetches the external URL associated with the IP. - /// @param ipId The canonical ID of the specified IP. - function uri(address ipId) external view returns (string memory) { - IP.MetadataV1 memory metadata = _metadata(ipId); - return metadata.uri; - } - - /// @notice Fetches the current owner of the IP. - /// @param ipId The canonical ID of the specified IP. - function owner(address ipId) public view returns (address) { - return IIPAccount(payable(ipId)).owner(); - } - - /// @notice Generates a JSON of all metadata attribution related to the IP. - /// TODO: Make this ERC-721 compatible, so that the IP registry may act as - /// an account-bound ERC-721 that points to this function for metadata. - /// TODO: Add SVG support. - /// TODO: Add licensing, royalties, and tagging information support. - function tokenURI(address ipId) external view returns (string memory) { - IP.MetadataV1 memory metadata = _metadata(ipId); - string memory baseJson = string( - /* solhint-disable */ - abi.encodePacked( - '{"name": "IP Asset #', - Strings.toHexString(ipId), - '", "description": "', - description(ipId), - '", "attributes": [' - ) - /* solhint-enable */ - ); - - string memory ipAttributes = string( - /* solhint-disable */ - abi.encodePacked( - '{"trait_type": "Name", "value": "', - metadata.name, - '"},' - '{"trait_type": "Owner", "value": "', - Strings.toHexString(owner(ipId)), - '"},' - '{"trait_type": "Registrant", "value": "', - Strings.toHexString(uint160(metadata.registrant), 20), - '"},', - '{"trait_type": "Hash", "value": "', - Strings.toHexString(uint256(metadata.hash), 32), - '"},', - '{"trait_type": "Registration Date", "value": "', - Strings.toString(metadata.registrationDate), - '"}' - ) - /* solhint-enable */ - ); - - return - string( - abi.encodePacked( - "data:application/json;base64,", - Base64.encode(bytes(string(abi.encodePacked(baseJson, ipAttributes, "]}")))) - ) - ); - } - - /// TODO: Add SVG generation support for branding within token metadata. - - /// @dev Internal function for fetching the metadata tied to an IP record. - function _metadata(address ipId) internal view returns (IP.MetadataV1 memory metadata) { - IMetadataProvider provider = IMetadataProvider(IP_ASSET_REGISTRY.metadataProvider(ipId)); - bytes memory data = provider.getMetadata(ipId); - if (data.length != 0) { - metadata = abi.decode(data, (IP.MetadataV1)); - } - } -} diff --git a/script/foundry/deployment/Main.s.sol b/script/foundry/deployment/Main.s.sol index 8eb180cd..76799444 100644 --- a/script/foundry/deployment/Main.s.sol +++ b/script/foundry/deployment/Main.s.sol @@ -26,7 +26,6 @@ import { IP_RESOLVER_MODULE_KEY, DISPUTE_MODULE_KEY, ROYALTY_MODULE_KEY, LICENSI import { IPMetadataProvider } from "contracts/registries/metadata/IPMetadataProvider.sol"; import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol"; import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol"; -import { IPAssetRenderer } from "contracts/registries/metadata/IPAssetRenderer.sol"; import { ModuleRegistry } from "contracts/registries/ModuleRegistry.sol"; import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; import { LicensingModule } from "contracts/modules/licensing/LicensingModule.sol"; @@ -84,7 +83,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { // Misc. Governance internal governance; AccessController internal accessController; - IPAssetRenderer internal ipAssetRenderer; IPResolver internal ipResolver; // Mocks @@ -186,15 +184,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { ); _postdeploy(contractKey, address(ipAssetRegistry)); - contractKey = "IPAssetRenderer"; - _predeploy(contractKey); - ipAssetRenderer = new IPAssetRenderer( - address(ipAssetRegistry), - address(licenseRegistry), - address(royaltyModule) - ); - _postdeploy(contractKey, address(ipAssetRenderer)); - contractKey = "RoyaltyModule"; _predeploy(contractKey); royaltyModule = new RoyaltyModule(address(governance)); diff --git a/test/foundry/integration/BaseIntegration.t.sol b/test/foundry/integration/BaseIntegration.t.sol index 79aa3ab1..629e236b 100644 --- a/test/foundry/integration/BaseIntegration.t.sol +++ b/test/foundry/integration/BaseIntegration.t.sol @@ -27,9 +27,7 @@ contract BaseIntegration is BaseTest { DeployModuleCondition({ disputeModule: true, royaltyModule: true, licensingModule: true }) ); buildDeployPolicyCondition(DeployPolicyCondition({ arbitrationPolicySP: true, royaltyPolicyLAP: true })); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: true, ipMetadataProvider: true, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: true, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/modules/ModuleBase.t.sol b/test/foundry/modules/ModuleBase.t.sol index 8b530cb1..8a33a2fb 100644 --- a/test/foundry/modules/ModuleBase.t.sol +++ b/test/foundry/modules/ModuleBase.t.sol @@ -14,9 +14,7 @@ abstract contract ModuleBaseTest is BaseTest { /// @notice Initializes the base module for testing. function setUp() public virtual override(BaseTest) { super.setUp(); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: false, ipMetadataProvider: false, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: false, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/modules/dispute/ArbitrationPolicySP.t.sol b/test/foundry/modules/dispute/ArbitrationPolicySP.t.sol index 6eb1c0e6..da569376 100644 --- a/test/foundry/modules/dispute/ArbitrationPolicySP.t.sol +++ b/test/foundry/modules/dispute/ArbitrationPolicySP.t.sol @@ -26,9 +26,7 @@ contract TestArbitrationPolicySP is BaseTest { DeployModuleCondition({ disputeModule: true, royaltyModule: false, licensingModule: false }) ); buildDeployPolicyCondition(DeployPolicyCondition({ arbitrationPolicySP: true, royaltyPolicyLAP: true })); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: false, ipMetadataProvider: false, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: false, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/modules/dispute/DisputeModule.t.sol b/test/foundry/modules/dispute/DisputeModule.t.sol index 82777a1d..f97355d2 100644 --- a/test/foundry/modules/dispute/DisputeModule.t.sol +++ b/test/foundry/modules/dispute/DisputeModule.t.sol @@ -46,9 +46,7 @@ contract DisputeModuleTest is BaseTest { DeployModuleCondition({ disputeModule: true, royaltyModule: false, licensingModule: false }) ); buildDeployPolicyCondition(DeployPolicyCondition({ arbitrationPolicySP: true, royaltyPolicyLAP: true })); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: false, ipMetadataProvider: false, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: false, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/modules/royalty/AncestorsVaultLAP.t.sol b/test/foundry/modules/royalty/AncestorsVaultLAP.t.sol index a1bc1aac..0f28f5ef 100644 --- a/test/foundry/modules/royalty/AncestorsVaultLAP.t.sol +++ b/test/foundry/modules/royalty/AncestorsVaultLAP.t.sol @@ -50,9 +50,7 @@ contract TestAncestorsVaultLAP is BaseTest { DeployModuleCondition({ disputeModule: false, royaltyModule: true, licensingModule: false }) ); buildDeployPolicyCondition(DeployPolicyCondition({ arbitrationPolicySP: false, royaltyPolicyLAP: true })); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: false, ipMetadataProvider: false, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: false, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/registries/metadata/IPAssetRenderer.t.sol b/test/foundry/registries/metadata/IPAssetRenderer.t.sol deleted file mode 100644 index 4ce33d8d..00000000 --- a/test/foundry/registries/metadata/IPAssetRenderer.t.sol +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.23; - -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; - -import { IP } from "contracts/lib/IP.sol"; - -import { BaseTest } from "test/foundry/utils/BaseTest.t.sol"; - -/// @title IP Asset Renderer Test Contract -/// @notice Tests IP asset rendering functionality. -/// TODO: Make this inherit module base to avoid code duplication. -contract IPAssetRendererTest is BaseTest { - // Default IP asset attributes. - string public constant IP_NAME = "IPAsset"; - string public constant IP_DESCRIPTION = "IPs all the way down."; - bytes32 public constant IP_HASH = "0x00"; - string public constant IP_EXTERNAL_URL = "https://storyprotocol.xyz"; - uint64 public IP_REGISTRATION_DATE; - - /// @notice Mock IP identifier for resolver testing. - address public ipId; - - /// @notice Initializes the base token contract for testing. - function setUp() public virtual override(BaseTest) { - super.setUp(); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: true, ipMetadataProvider: false, ipResolver: true }) - ); - deployConditionally(); - postDeploymentSetup(); - - IP_REGISTRATION_DATE = uint64(block.timestamp); - - vm.startPrank(alice); - uint256 tokenId = mockNFT.mintId(alice, 99); - bytes memory metadata = abi.encode( - IP.MetadataV1({ - name: IP_NAME, - hash: IP_HASH, - registrationDate: IP_REGISTRATION_DATE, - registrant: alice, - uri: IP_EXTERNAL_URL - }) - ); - ipId = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenId, address(ipResolver), true, metadata); - vm.stopPrank(); - } - - /// @notice Tests that the constructor works as expected. - function test_IPAssetRenderer_Constructor() public virtual { - assertEq(address(ipAssetRenderer.IP_ASSET_REGISTRY()), address(ipAssetRegistry)); - } - - /// @notice Tests that ipAssetRenderer can properly resolve names. - function test_IPAssetRenderer_Name() public virtual { - assertEq(ipAssetRenderer.name(ipId), IP_NAME); - } - - /// @notice Tests that the ipAssetRenderer can properly resolve descriptions. - function test_IPAssetRenderer_Description() public virtual { - assertEq( - ipAssetRenderer.description(ipId), - string.concat( - IP_NAME, - ", IP #", - Strings.toHexString(ipId), - ", is currently owned by", - Strings.toHexString(alice), - ". To learn more about this IP, visit ", - IP_EXTERNAL_URL - ) - ); - } - - /// @notice Tests that ipAssetRenderer can properly resolve hashes. - function test_IPAssetRenderer_Hash() public virtual { - assertEq(ipAssetRenderer.hash(ipId), IP_HASH); - } - - /// @notice Tests that ipAssetRenderer can properly resolve registration dates. - function test_IPAssetRenderer_RegistrationDate() public virtual { - assertEq(uint256(ipAssetRenderer.registrationDate(ipId)), uint256(IP_REGISTRATION_DATE)); - } - - /// @notice Tests that ipAssetRenderer can properly resolve registrants. - function test_IPAssetRenderer_Registrant() public virtual { - assertEq(ipAssetRenderer.registrant(ipId), alice); - } - - /// @notice Tests that ipAssetRenderer can properly resolve URLs. - function test_IPAssetRenderer_ExternalURL() public virtual { - assertEq(ipAssetRenderer.uri(ipId), IP_EXTERNAL_URL); - } - - /// @notice Tests that ipAssetRenderer can properly owners. - function test_IPAssetRenderer_Owner() public virtual { - assertEq(ipAssetRenderer.owner(ipId), alice); - } - - /// @notice Tests that the ipAssetRenderer can get the right token URI. - function test_IPAssetRenderer_TokenURI() public virtual { - string memory ownerStr = Strings.toHexString(uint160(address(alice))); - string memory description = string.concat( - IP_NAME, - ", IP #", - Strings.toHexString(ipId), - ", is currently owned by", - Strings.toHexString(alice), - ". To learn more about this IP, visit ", - IP_EXTERNAL_URL - ); - - string memory ipIdStr = Strings.toHexString(uint160(ipId)); - /* solhint-disable */ - string memory uriEncoding = string( - abi.encodePacked( - '{"name": "IP Asset #', - ipIdStr, - '", "description": "', - description, - '", "attributes": [', - '{"trait_type": "Name", "value": "IPAsset"},', - '{"trait_type": "Owner", "value": "', - ownerStr, - '"},' - '{"trait_type": "Registrant", "value": "', - ownerStr, - '"},', - '{"trait_type": "Hash", "value": "0x3078303000000000000000000000000000000000000000000000000000000000"},', - '{"trait_type": "Registration Date", "value": "', - Strings.toString(IP_REGISTRATION_DATE), - '"}', - "]}" - ) - ); - /* solhint-enable */ - string memory expectedURI = string( - abi.encodePacked( - "data:application/json;base64,", - Base64.encode(bytes(string(abi.encodePacked(uriEncoding)))) - ) - ); - assertEq(expectedURI, ipAssetRenderer.tokenURI(ipId)); - } -} diff --git a/test/foundry/registries/metadata/MetadataProvider.t.sol b/test/foundry/registries/metadata/MetadataProvider.t.sol index 122c225a..3f5024c5 100644 --- a/test/foundry/registries/metadata/MetadataProvider.t.sol +++ b/test/foundry/registries/metadata/MetadataProvider.t.sol @@ -86,9 +86,7 @@ contract MetadataProviderTest is BaseTest { buildDeployModuleCondition( DeployModuleCondition({ disputeModule: false, royaltyModule: false, licensingModule: false }) ); - buildDeployMiscCondition( - DeployMiscCondition({ ipAssetRenderer: false, ipMetadataProvider: false, ipResolver: true }) - ); + buildDeployMiscCondition(DeployMiscCondition({ ipMetadataProvider: false, ipResolver: true })); deployConditionally(); postDeploymentSetup(); diff --git a/test/foundry/utils/DeployHelper.t.sol b/test/foundry/utils/DeployHelper.t.sol index e7e7219c..9bec60a0 100644 --- a/test/foundry/utils/DeployHelper.t.sol +++ b/test/foundry/utils/DeployHelper.t.sol @@ -20,7 +20,6 @@ import { IPAccountImpl } from "../../../contracts/IPAccountImpl.sol"; import { IPMetadataProvider } from "../../../contracts/registries/metadata/IPMetadataProvider.sol"; import { IPAccountRegistry } from "../../../contracts/registries/IPAccountRegistry.sol"; import { IPAssetRegistry } from "../../../contracts/registries/IPAssetRegistry.sol"; -import { IPAssetRenderer } from "../../../contracts/registries/metadata/IPAssetRenderer.sol"; import { ModuleRegistry } from "../../../contracts/registries/ModuleRegistry.sol"; import { LicenseRegistry } from "../../../contracts/registries/LicenseRegistry.sol"; import { IPResolver } from "../../../contracts/resolvers/IPResolver.sol"; @@ -44,7 +43,6 @@ import { MockERC20 } from "../mocks/token/MockERC20.sol"; import { MockERC721 } from "../mocks/token/MockERC721.sol"; import { TestProxyHelper } from "./TestProxyHelper.sol"; - contract DeployHelper { // TODO: three options, auto/mock/real in deploy condition, so that we don't need to manually // call getXXX to get mock contract (if there's no real contract deployed). @@ -73,7 +71,6 @@ contract DeployHelper { } struct DeployMiscCondition { - bool ipAssetRenderer; bool ipMetadataProvider; bool ipResolver; } @@ -126,7 +123,6 @@ contract DeployHelper { RoyaltyPolicyLAP internal royaltyPolicyLAP; // Misc. - IPAssetRenderer internal ipAssetRenderer; IPMetadataProvider internal ipMetadataProvider; IPResolver internal ipResolver; @@ -181,7 +177,7 @@ contract DeployHelper { buildDeployModuleCondition(DeployModuleCondition(true, true, true)); buildDeployAccessCondition(DeployAccessCondition(true, true)); buildDeployPolicyCondition(DeployPolicyCondition(true, true)); - buildDeployMiscCondition(DeployMiscCondition(true, true, true)); + buildDeployMiscCondition(DeployMiscCondition(true, true)); deployConditionally(); } @@ -331,11 +327,6 @@ contract DeployHelper { function _deployMiscConditionally(DeployMiscCondition memory d) public { // Skip IPResolver here, called in `_deployIPResolverConditionally` - if (d.ipAssetRenderer) { - require(address(ipAssetRegistry) != address(0), "DeployHelper Misc: IPAssetRegistry required"); - ipAssetRenderer = new IPAssetRenderer(address(ipAssetRegistry), getLicenseRegistry(), getRoyaltyModule()); - console2.log("DeployHelper: Using REAL IPAssetRenderer"); - } if (d.ipMetadataProvider) { ipMetadataProvider = new IPMetadataProvider(getModuleRegistry()); console2.log("DeployHelper: Using REAL IPMetadataProvider");