Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing IPAccount Namespace Storage and IViewModule for Enhanced Metadata Display #2

Merged
merged 10 commits into from
Mar 15, 2024
9 changes: 5 additions & 4 deletions contracts/IPAccountImpl.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
Expand All @@ -13,10 +13,11 @@ import { IAccessController } from "./interfaces/IAccessController.sol";
import { IIPAccount } from "./interfaces/IIPAccount.sol";
import { MetaTx } from "./lib/MetaTx.sol";
import { Errors } from "./lib/Errors.sol";
import { IPAccountStorage } from "./IPAccountStorage.sol";

/// @title IPAccountImpl
/// @notice The Story Protocol's implementation of the IPAccount.
contract IPAccountImpl is IERC165, IIPAccount {
contract IPAccountImpl is IPAccountStorage, IIPAccount {
address public immutable accessController;

/// @notice Returns the IPAccount's internal nonce for transaction ordering.
Expand All @@ -38,12 +39,12 @@ contract IPAccountImpl is IERC165, IIPAccount {
/// @notice Checks if the contract supports a specific interface
/// @param interfaceId The interface identifier, as specified in ERC-165
/// @return bool is true if the contract supports the interface, false otherwise
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
function supportsInterface(bytes4 interfaceId) public view override(IPAccountStorage, IERC165) returns (bool) {
return (interfaceId == type(IIPAccount).interfaceId ||
interfaceId == type(IERC6551Account).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC721Receiver).interfaceId ||
interfaceId == type(IERC165).interfaceId);
super.supportsInterface(interfaceId));
}

/// @notice Returns the identifier of the non-fungible token which owns the account
Expand Down
61 changes: 61 additions & 0 deletions contracts/IPAccountStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import { IIPAccountStorage } from "./interfaces/IIPAccountStorage.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
/// @title IPAccount Storage
/// @dev Implements the IIPAccountStorage interface for managing IPAccount's state using a namespaced storage pattern.
/// Inherits all functionalities from IIPAccountStorage, providing concrete implementations for the interface's methods.
/// This contract allows Modules to store and retrieve data in a structured and conflict-free manner
/// by utilizing namespaces, where the default namespace is determined by the
/// `msg.sender` (the caller Module's address).
contract IPAccountStorage is ERC165, IIPAccountStorage {
using ShortStrings for *;

mapping(bytes32 => mapping(bytes32 => string)) public stringData;
mapping(bytes32 => mapping(bytes32 => bytes)) public bytesData;
mapping(bytes32 => mapping(bytes32 => bytes32)) public bytes32Data;
mapping(bytes32 => mapping(bytes32 => uint256)) public uint256Data;
mapping(bytes32 => mapping(bytes32 => address)) public addressData;
mapping(bytes32 => mapping(bytes32 => bool)) public boolData;

/// @inheritdoc IIPAccountStorage
function setBytes(bytes32 key, bytes calldata value) external {
bytesData[_toBytes32(msg.sender)][key] = value;
}
/// @inheritdoc IIPAccountStorage
function getBytes(bytes32 key) external view returns (bytes memory) {
return bytesData[_toBytes32(msg.sender)][key];
}
/// @inheritdoc IIPAccountStorage
function getBytes(bytes32 namespace, bytes32 key) external view returns (bytes memory) {
return bytesData[namespace][key];
}

/// @inheritdoc IIPAccountStorage
function setBytes32(bytes32 key, bytes32 value) external {
bytes32Data[_toBytes32(msg.sender)][key] = value;
}
/// @inheritdoc IIPAccountStorage
function getBytes32(bytes32 key) external view returns (bytes32) {
return bytes32Data[_toBytes32(msg.sender)][key];
}
/// @inheritdoc IIPAccountStorage
function getBytes32(bytes32 namespace, bytes32 key) external view returns (bytes32) {
return bytes32Data[namespace][key];
}

/// @notice ERC165 interface identifier for IIPAccountStorage
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
return interfaceId == type(IIPAccountStorage).interfaceId || super.supportsInterface(interfaceId);
}

function _toBytes32(string memory s) internal pure returns (bytes32) {
return ShortString.unwrap(s.toShortString());
}

function _toBytes32(address a) internal pure returns (bytes32) {
return bytes32(uint256(uint160(a)));
}
}
3 changes: 2 additions & 1 deletion contracts/interfaces/IIPAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.23;
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol";
import { IIPAccountStorage } from "./IIPAccountStorage.sol";

/// @title IIPAccount
/// @dev IPAccount is a token-bound account that adopts the EIP-6551 standard.
Expand All @@ -13,7 +14,7 @@ import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol";
/// IPAccount can interact with modules by making calls as a normal transaction sender.
/// This allows for seamless operations on the state and data of IP.
/// IPAccount is core identity for all actions.
interface IIPAccount is IERC6551Account, IERC721Receiver, IERC1155Receiver {
interface IIPAccount is IERC6551Account, IERC721Receiver, IERC1155Receiver, IIPAccountStorage {
/// @notice Emitted when a transaction is executed.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether sent.
Expand Down
50 changes: 50 additions & 0 deletions contracts/interfaces/IIPAccountStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;

/// @title IPAccount Namespaced Storage Interface
/// @dev Provides a structured way to store IPAccount's state using a namespaced storage pattern.
/// This interface facilitates conflict-free data writing by different Modules into the same IPAccount
/// by utilizing namespaces.
/// The default namespace for write operations is determined by the `msg.sender`, ensuring that only the owning Module
/// (i.e., the Module calling the write functions) can write data into its respective namespace.
/// However, read operations are unrestricted and can access any namespace.
///
/// Rules:
/// - The default namespace for a Module is its own address.
/// - Every Module can read data from any namespace.
/// - Only the owning Module (i.e., the Module whose address is used as the namespace) can write data into
/// its respective namespace.
interface IIPAccountStorage {
/// @dev Sets a bytes value under a given key within the default namespace, determined by `msg.sender`.
/// @param key The key under which to store the value.
/// @param value The bytes value to be stored.
function setBytes(bytes32 key, bytes calldata value) external;

/// @dev Retrieves a bytes value by a given key from the default namespace.
/// @param key The key whose value is to be retrieved.
/// @return The bytes value stored under the specified key.
function getBytes(bytes32 key) external view returns (bytes memory);

/// @dev Retrieves a bytes value by a given key from a specified namespace.
/// @param namespace The namespace from which to retrieve the value.
/// @param key The key whose value is to be retrieved.
/// @return The bytes value stored under the specified key in the given namespace.
function getBytes(bytes32 namespace, bytes32 key) external view returns (bytes memory);

/// @dev Sets a bytes32 value under a given key within the default namespace, determined by `msg.sender`.
/// @param key The key under which to store the value.
/// @param value The bytes32 value to be stored.
function setBytes32(bytes32 key, bytes32 value) external;

/// @dev Retrieves a bytes32 value by a given key from the default namespace.
/// @param key The key whose value is to be retrieved.
/// @return The bytes32 value stored under the specified key.
function getBytes32(bytes32 key) external view returns (bytes32);

/// @dev Retrieves a bytes32 value by a given key from a specified namespace.
/// @param namespace The namespace from which to retrieve the value.
/// @param key The key whose value is to be retrieved.
/// @return The bytes32 value stored under the specified key in the given namespace.
function getBytes32(bytes32 namespace, bytes32 key) external view returns (bytes32);
}
17 changes: 17 additions & 0 deletions contracts/interfaces/modules/base/IViewModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;

import { IModule } from "./IModule.sol";

/// @notice View Module Interface
/// View modules typically are read-only modules that are responsible for displaying
/// IP-related data in various ways to meet different needs.For instance,
/// they can display simple/base/core metadata, book specific metadata, license details metadata,
/// or even IP graph data for the same IPAccount using different View Modules.
/// This module offers flexibility in selecting which data to display and how to present it.
/// @dev View Module can read data from IPAccount and from multiple namesapces to combine data for display.
interface IViewModule is IModule {
/// @notice check whether the view module is supported for the given IP account
function isSupported(address ipAccount) external returns (bool);
}
Loading