Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
IPAssetRenderer
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 20000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
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 { IIPAccount } from "contracts/interfaces/IIPAccount.sol";
import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol";
import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol";
import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol";
import { TaggingModule } from "contracts/modules/tagging/TaggingModule.sol";
import { RoyaltyModule } from "contracts/modules/royalty-module/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.
TaggingModule public immutable TAGGING_MODULE;
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 taggingModule,
address royaltyModule
) {
IP_ASSET_REGISTRY = IPAssetRegistry(assetRegistry);
LICENSE_REGISTRY = LicenseRegistry(licenseRegistry);
TAGGING_MODULE = TaggingModule(taggingModule);
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));
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Base64.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
/// @title IP Library
/// @notice Library for constants, structs, and helper functions used for IP.
library IP {
/// @notice Core metadata to associate with each IP.
struct MetadataV1 {
// The name associated with the IP.
string name;
// A keccak-256 hash of the IP content.
bytes32 hash;
// The date which the IP was registered.
uint64 registrationDate;
// The address of the initial IP registrant.
address registrant;
// An external URI associated with the IP.
string uri;
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
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 "lib/reference/src/interfaces/IERC6551Account.sol";
/// @title IIPAccount
/// @dev IPAccount is a token-bound account that adopts the EIP-6551 standard.
/// These accounts are deployed at deterministic addresses through the official 6551 account registry.
/// As a deployed smart contract, IPAccount can store IP-related information,
/// like ownership of other NFTs such as license NFT or Royalty NFT.
/// 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 {
/// @notice Emitted when a transaction is executed.
event Executed(address indexed to, uint256 value, bytes data, uint256 nonce);
/// @notice Emitted when a transaction is executed on behalf of the signer.
event ExecutedWithSig(
address indexed to,
uint256 value,
bytes data,
uint256 nonce,
uint256 deadline,
address indexed signer,
bytes signature
);
/// @notice Executes a transaction from the IP Account.
/// @param to_ The recipient of the transaction.
/// @param value_ The amount of Ether to send.
/// @param data_ The data to send along with the transaction.
/// @return The return data from the transaction.
function execute(address to_, uint256 value_, bytes calldata data_) external payable returns (bytes memory);
/// @notice Executes a transaction from the IP Account on behalf of the signer.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether to send.
/// @param data The data to send along with the transaction.
/// @param signer The signer of the transaction.
/// @param deadline The deadline of the transaction signature.
/// @param signature The signature of the transaction, EIP-712 encoded.
function executeWithSig(
address to,
uint256 value,
bytes calldata data,
address signer,
uint256 deadline,
bytes calldata signature
) external payable returns (bytes memory);
/// @notice Returns the owner of the IP Account.
/// @return The address of the owner.
function owner() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { IIPAccount } from "contracts/interfaces/IIPAccount.sol";
import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol";
import { IIPAssetRegistry } from "contracts/interfaces/registries/IIPAssetRegistry.sol";
import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol";
import { IMetadataProviderMigratable } from "contracts/interfaces/registries/metadata/IMetadataProviderMigratable.sol";
import { MetadataProviderV1 } from "contracts/registries/metadata/MetadataProviderV1.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { IResolver } from "contracts/interfaces/resolvers/IResolver.sol";
/// @title IP Asset Registry
/// @notice This contract acts as the source of truth for all IP registered in
/// Story Protocol. An IP is identified by its contract address, token
/// id, and coin type, meaning any NFT may be conceptualized as an IP.
/// Once an IP is registered into the protocol, a corresponding IP
/// asset is generated, which references an IP resolver for metadata
/// attribution and an IP account for protocol authorization.
/// IMPORTANT: The IP account address, besides being used for protocol
/// auth, is also the canonical IP identifier for the IP NFT.
contract IPAssetRegistry is IIPAssetRegistry, IPAccountRegistry {
/// @notice Attributes for the IP asset type.
struct Record {
// Metadata provider for Story Protocol canonicalized metadata.
IMetadataProviderMigratable metadataProvider;
address resolver;
}
/// @notice Tracks the total number of IP assets in existence.
uint256 public totalSupply = 0;
/// @notice Protocol governance administrator of the IP record registry.
address owner;
/// @dev Maps an IP, identified by its IP ID, to an IP record.
mapping(address => Record) internal _records;
/// @notice Tracks the current metadata provider used for IP registrations.
IMetadataProviderMigratable internal _metadataProvider;
/// @notice Ensures only protocol governance owner may call a function.
modifier onlyOwner() {
if (msg.sender != owner) {
revert Errors.IPAssetRegistry__Unauthorized();
}
_;
}
/// @notice Initializes the IP Asset Registry.
/// @param erc6551Registry The address of the ERC6551 registry.
/// @param accessController The address of the access controller.
/// @param ipAccountImpl The address of the IP account implementation.
constructor(
address accessController,
address erc6551Registry,
address ipAccountImpl
) IPAccountRegistry(erc6551Registry, accessController, ipAccountImpl) {
// TODO: Migrate this to a parameterized governance owner address.
owner = msg.sender;
_metadataProvider = IMetadataProviderMigratable(new MetadataProviderV1(address(this)));
}
/// @notice Registers an NFT as an IP, creating a corresponding IP asset.
/// @param chainId The chain identifier of where the NFT resides.
/// @param tokenContract The address of the NFT.
/// @param tokenId The token identifier of the NFT.
/// @param createAccount Whether to create an IP account when registering.
function register(
uint256 chainId,
address tokenContract,
uint256 tokenId,
address resolverAddr,
bool createAccount,
bytes calldata data
) external returns (address id) {
id = ipId(chainId, tokenContract, tokenId);
if (_records[id].resolver != address(0)) {
revert Errors.IPAssetRegistry__AlreadyRegistered();
}
if (id.code.length == 0 && createAccount && id != registerIpAccount(chainId, tokenContract, tokenId)) {
revert Errors.IPAssetRegistry__InvalidAccount();
}
_setResolver(id, resolverAddr);
_setMetadata(id, _metadataProvider, data);
totalSupply++;
emit IPRegistered(id, chainId, tokenContract, tokenId, resolverAddr, address(_metadataProvider), data);
}
/// @notice Gets the canonical IP identifier associated with an IP NFT.
/// @dev This is equivalent to the address of its bound IP account.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @return The IP's canonical address identifier.
function ipId(uint256 chainId, address tokenContract, uint256 tokenId) public view returns (address) {
return super.ipAccount(chainId, tokenContract, tokenId);
}
/// @notice Checks whether an IP was registered based on its ID.
/// @param id The canonical identifier for the IP.
/// @return Whether the IP was registered into the protocol.
function isRegistered(address id) external view returns (bool) {
return _records[id].resolver != address(0);
}
/// @notice Gets the resolver bound to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The IP resolver address if registered, else the zero address.
function resolver(address id) external view returns (address) {
return _records[id].resolver;
}
/// @notice Gets the metadata provider used for new metadata registrations.
/// @return The address of the metadata provider used for new IP registrations.
function metadataProvider() external view returns (address) {
return address(_metadataProvider);
}
/// @notice Gets the metadata provider linked to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The metadata provider that was bound to this IP at creation time.
function metadataProvider(address id) external view returns (address) {
return address(_records[id].metadataProvider);
}
/// @notice Gets the underlying canonical metadata linked to an IP asset.
/// @param id The canonical ID of the IP asset.
function metadata(address id) external view returns (bytes memory) {
if (address(_records[id].metadataProvider) == address(0)) {
revert Errors.IPAssetRegistry__NotYetRegistered();
}
return _records[id].metadataProvider.getMetadata(id);
}
/// @notice Sets the provider for storage of new IP metadata, while enabling
/// existing IP assets to migrate their metadata to the new provider.
/// @param newMetadataProvider Address of the new metadata provider contract.
function setMetadataProvider(address newMetadataProvider) external onlyOwner {
_metadataProvider.setUpgradeProvider(newMetadataProvider);
_metadataProvider = IMetadataProviderMigratable(newMetadataProvider);
}
/// @notice Sets the underlying metadata for an IP asset.
/// @dev As metadata is immutable but additive, this will only be used when
/// an IP migrates from a new provider that introduces new attributes.
/// @param id The canonical ID of the IP.
/// @param data Canonical metadata to associate with the IP.
function setMetadata(address id, address provider, bytes calldata data) external {
// Metadata is set on registration and immutable thereafter, with new fields
// only added during a migration to new protocol-approved metadata provider.
if (address(_records[id].metadataProvider) != msg.sender) {
revert Errors.IPAssetRegistry__Unauthorized();
}
_setMetadata(id, IMetadataProviderMigratable(provider), data);
}
/// @notice Sets the resolver for an IP based on its canonical ID.
/// @param id The canonical ID of the IP.
/// @param resolverAddr The address of the resolver being set.
function setResolver(address id, address resolverAddr) public {
if (_records[id].resolver == address(0)) {
revert Errors.IPAssetRegistry__NotYetRegistered();
}
// TODO: Update authorization logic to use the access controller.
if (msg.sender != IIPAccount(payable(id)).owner()) {
revert Errors.IPAssetRegistry__Unauthorized();
}
_setResolver(id, resolverAddr);
}
/// @dev Sets the resolver for the specified IP.
/// @param id The canonical ID of the IP.
/// @param resolverAddr The address of the resolver being set.
function _setResolver(address id, address resolverAddr) internal {
ERC165Checker.supportsInterface(resolverAddr, type(IResolver).interfaceId);
_records[id].resolver = resolverAddr;
emit IPResolverSet(id, resolverAddr);
}
/// @dev Sets the for the specified IP asset.
/// @param id The canonical identifier for the specified IP asset.
/// @param provider The metadata provider hosting the data.
/// @param data The metadata to set for the IP asset.
function _setMetadata(address id, IMetadataProviderMigratable provider, bytes calldata data) internal {
_records[id].metadataProvider = provider;
provider.setMetadata(id, data);
emit MetadataSet(id, address(provider), data);
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
/// @title Metadata Provider Interface
interface IMetadataProvider {
/// @notice Emits when canonical metadata was set for a specific IP asset.
event MetadataSet(address ipId, bytes metadata);
/// @notice Gets the metadata associated with an IP asset.
/// @param ipId The address identifier of the IP asset.
function getMetadata(address ipId) external view returns (bytes memory);
/// @notice Sets the metadata associated with an IP asset.
/// @param ipId The address identifier of the IP asset.
/// @param metadata Metadata in bytes to associate with the IP asset.
function setMetadata(address ipId, bytes memory metadata) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
// external
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
// contracts
import { ShortStringOps } from "contracts/utils/ShortStringOps.sol";
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol";
import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol";
import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol";
import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol";
import { ILicenseRegistry } from "contracts/interfaces/registries/ILicenseRegistry.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { Licensing } from "contracts/lib/Licensing.sol";
import { IPolicyFrameworkManager } from "contracts/interfaces/licensing/IPolicyFrameworkManager.sol";
import { IAccessController } from "contracts/interfaces/IAccessController.sol";
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { IPAccountChecker } from "contracts/lib/registries/IPAccountChecker.sol";
// TODO: consider disabling operators/approvals on creation
contract LicenseRegistry is ERC1155, ILicenseRegistry {
using IPAccountChecker for IIPAccountRegistry;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
using Strings for *;
using ShortStrings for *;
using Licensing for *;
struct PolicySetup {
uint256 index;
bool isSet;
bool active;
bool inheritedPolicy;
}
IAccessController public immutable ACCESS_CONTROLLER;
IIPAccountRegistry public immutable IP_ACCOUNT_REGISTRY;
mapping(address framework => bool registered) private _registeredFrameworkManagers;
mapping(bytes32 policyHash => uint256 policyId) private _hashedPolicies;
mapping(uint256 policyId => Licensing.Policy policyData) private _policies;
uint256 private _totalPolicies;
/// @notice internal mapping to track if a policy was set by linking or minting, and the
/// index of the policy in the ipId policy set
/// Policies can't be removed, but they can be deactivated by setting active to false
mapping(address ipId => mapping(uint256 policyId => PolicySetup setup)) private _policySetups;
mapping(address ipId => EnumerableSet.UintSet policyIds) private _policiesPerIpId;
mapping(address ipId => EnumerableSet.AddressSet parentIpIds) private _ipIdParents;
mapping(bytes32 licenseHash => uint256 ids) private _hashedLicenses;
mapping(uint256 licenseIds => Licensing.License licenseData) private _licenses;
/// This tracks the number of licenses registered in the protocol, it will not decrease when a license is burnt.
uint256 private _totalLicenses;
constructor(address accessController, address ipAccountRegistry) ERC1155("") {
ACCESS_CONTROLLER = IAccessController(accessController);
IP_ACCOUNT_REGISTRY = IIPAccountRegistry(ipAccountRegistry);
}
/// @notice registers a policy framework manager into the contract, so it can add policy data for
/// licenses.
/// @param manager the address of the manager. Will be ERC165 checked for IPolicyFrameworkManager
function registerPolicyFrameworkManager(address manager) external {
if (!ERC165Checker.supportsInterface(manager, type(IPolicyFrameworkManager).interfaceId)) {
revert Errors.LicenseRegistry__InvalidPolicyFramework();
}
IPolicyFrameworkManager fwManager = IPolicyFrameworkManager(manager);
string memory licenseUrl = fwManager.licenseTextUrl();
if (bytes(licenseUrl).length == 0 || licenseUrl.equal("")) {
revert Errors.LicenseRegistry__EmptyLicenseUrl();
}
_registeredFrameworkManagers[manager] = true;
emit PolicyFrameworkRegistered(manager, fwManager.name(), licenseUrl);
}
/// Adds a policy to an ipId, which can be used to mint licenses.
/// Licnses are permissions for ipIds to be derivatives (children).
/// if policyId is not defined in LicenseRegistry, reverts.
/// Will revert if ipId already has the same policy
/// @param ipId to receive the policy
/// @param polId id of the policy data
/// @return indexOnIpId position of policy within the ipIds policy set
function addPolicyToIp(address ipId, uint256 polId) external returns (uint256 indexOnIpId) {
if (
msg.sender != ipId &&
!ACCESS_CONTROLLER.checkPermission(ipId, msg.sender, address(this), this.addPolicyToIp.selector)
) {
revert Errors.LicenseRegistry__UnauthorizedAccess();
}
if (!isPolicyDefined(polId)) {
revert Errors.LicenseRegistry__PolicyNotFound();
}
return _addPolicyIdToIp(ipId, polId, false);
}
/// @notice Registers a policy into the contract. MUST be called by a registered
/// framework or it will revert. The policy data and its integrity must be
/// verified by the policy framework manager.
/// @param data The policy data
function registerPolicy(bytes memory data) external returns (uint256 policyId) {
_verifyRegisteredFramework(address(msg.sender));
Licensing.Policy memory pol = Licensing.Policy({ policyFramework: msg.sender, data: data });
(uint256 polId, bool newPol) = _addIdOrGetExisting(abi.encode(pol), _hashedPolicies, _totalPolicies);
if (!newPol) {
revert Errors.LicenseRegistry__PolicyAlreadyAdded();
} else {
_totalPolicies = polId;
_policies[polId] = pol;
emit PolicyRegistered(msg.sender, polId, data);
}
return polId;
}
/// Mints license NFTs representing a policy granted by a set of ipIds (licensors). This NFT needs to be burned
/// in order to link a derivative IP with its parents.
/// If this is the first combination of policy and licensors, a new licenseId
/// will be created (by incrementing prev totalLicenses).
/// If not, the license is fungible and an id will be reused.
/// The licensing terms that regulate creating new licenses will be verified to allow minting.
/// Reverts if caller is not authorized by licensors.
/// @param policyId id of the policy to be minted
/// @param licensorIp IP Id granting the license
/// @param amount of licenses to be minted. License NFT is fungible for same policy and same licensors
/// @param receiver of the License NFT(s).
/// @return licenseId of the NFT(s).
function mintLicense(
uint256 policyId,
address licensorIp,
uint256 amount, // mint amount
address receiver
) external returns (uint256 licenseId) {
// TODO: check if licensor has been tagged by disputer
if (!IP_ACCOUNT_REGISTRY.isIpAccount(licensorIp)) {
revert Errors.LicenseRegistry__LicensorNotRegistered();
}
// If the IP ID doesn't have a policy (meaning, no permissionless derivatives)
if (!_policiesPerIpId[licensorIp].contains(policyId)) {
// We have to check if the caller is licensor or authorized to mint.
if (
msg.sender != licensorIp &&
!ACCESS_CONTROLLER.checkPermission(licensorIp, msg.sender, address(this), this.mintLicense.selector)
) {
revert Errors.LicenseRegistry__CallerNotLicensorAndPolicyNotSet();
}
}
// If a policy is set, then is only up to the policy params.
// Verify minting param
Licensing.Policy memory pol = policy(policyId);
bool inheritedPolicy = _policySetups[licensorIp][policyId].inheritedPolicy;
if (ERC165Checker.supportsInterface(pol.policyFramework, type(IMintParamVerifier).interfaceId)) {
if (
!IMintParamVerifier(pol.policyFramework).verifyMint(
msg.sender,
inheritedPolicy,
licensorIp,
receiver,
amount,
pol.data
)
) {
revert Errors.LicenseRegistry__MintLicenseParamFailed();
}
}
Licensing.License memory licenseData = Licensing.License({ policyId: policyId, licensorIpId: licensorIp });
bool isNew;
(licenseId, isNew) = _addIdOrGetExisting(abi.encode(licenseData), _hashedLicenses, _totalLicenses);
if (isNew) {
_totalLicenses = licenseId;
_licenses[licenseId] = licenseData;
emit LicenseMinted(msg.sender, receiver, licenseId, amount, licenseData);
}
_mint(receiver, licenseId, amount, "");
return licenseId;
}
/// @notice Links an IP to the licensors (parent IP IDs) listed in the License NFTs, if their policies allow it,
/// burning the NFTs in the proccess. The caller must be the owner of the NFTs and the IP owner.
/// @param licenseIds The id of the licenses to burn
/// @param childIpId The id of the child IP to be linked
/// @param holder The address that holds the license
function linkIpToParents(uint256[] calldata licenseIds, address childIpId, address holder) external {
// check the caller is owner or authorized by the childIp
if (
msg.sender != childIpId &&
!ACCESS_CONTROLLER.checkPermission(childIpId, msg.sender, address(this), this.linkIpToParents.selector)
) {
revert Errors.LicenseRegistry__UnauthorizedAccess();
}
uint256 licenses = licenseIds.length;
address[] memory licensors = new address[](licenses);
uint256[] memory values = new uint256[](licenses);
for (uint256 i = 0; i < licenses; i++) {
uint256 licenseId = licenseIds[i];
if (balanceOf(holder, licenseId) == 0) {
revert Errors.LicenseRegistry__NotLicensee();
}
Licensing.License memory licenseData = _licenses[licenseId];
licensors[i] = licenseData.licensorIpId;
// TODO: check licensor not part of a branch tagged by disputer
if (licensors[i] == childIpId) {
revert Errors.LicenseRegistry__ParentIdEqualThanChild();
}
// Verify linking params
Licensing.Policy memory pol = policy(licenseData.policyId);
if (ERC165Checker.supportsInterface(pol.policyFramework, type(ILinkParamVerifier).interfaceId)) {
if (
!ILinkParamVerifier(pol.policyFramework).verifyLink(
licenseId,
msg.sender,
childIpId,
licensors[i],
pol.data
)
) {
revert Errors.LicenseRegistry__LinkParentParamFailed();
}
}
// Add policy to kid
_addPolicyIdToIp(childIpId, licenseData.policyId, true);
// Set parent
_ipIdParents[childIpId].add(licensors[i]);
values[i] = 1;
}
emit IpIdLinkedToParents(msg.sender, childIpId, licensors);
// Burn licenses
_burnBatch(holder, licenseIds, values);
}
/// @notice True if the framework address is registered in LicenseRegistry
function isFrameworkRegistered(address policyFramework) external view returns (bool) {
return _registeredFrameworkManagers[policyFramework];
}
function name() external pure returns (string memory) {
return "LICENSE_REGISTRY";
}
/// Returns true if holder has positive balance for licenseId
function isLicensee(uint256 licenseId, address holder) external view returns (bool) {
return balanceOf(holder, licenseId) > 0;
}
function policyIdForLicense(uint256 licenseId) external view returns (uint256) {
return _licenses[licenseId].policyId;
}
function policyForLicense(uint256 licenseId) public view returns (Licensing.Policy memory) {
return policy(_licenses[licenseId].policyId);
}
/// Returns amount of distinct licensing policies in LicenseRegistry
function totalPolicies() external view returns (uint256) {
return _totalPolicies;
}
/// Gets policy data for policyId, reverts if not found
function policy(uint256 policyId) public view returns (Licensing.Policy memory pol) {
pol = _policies[policyId];
_verifyPolicy(pol);
return pol;
}
/// Returns true if policyId is defined in LicenseRegistry, false otherwise.
function isPolicyDefined(uint256 policyId) public view returns (bool) {
return _policies[policyId].policyFramework != address(0);
}
/// Gets the policy set for an IpId
/// @dev potentially expensive operation, use with care
function policyIdsForIp(address ipId) external view returns (uint256[] memory policyIds) {
return _policiesPerIpId[ipId].values();
}
function totalPoliciesForIp(address ipId) external view returns (uint256) {
return _policiesPerIpId[ipId].length();
}
function isPolicyIdSetForIp(address ipId, uint256 policyId) external view returns (bool) {
return _policiesPerIpId[ipId].contains(policyId);
}
function policyIdForIpAtIndex(address ipId, uint256 index) external view returns (uint256 policyId) {
return _policiesPerIpId[ipId].at(index);
}
function policyForIpAtIndex(address ipId, uint256 index) external view returns (Licensing.Policy memory) {
return _policies[_policiesPerIpId[ipId].at(index)];
}
function indexOfPolicyForIp(address ipId, uint256 policyId) external view returns (uint256 index) {
return _policySetups[ipId][policyId].index;
}
function isPolicyInherited(address ipId, uint256 policyId) external view returns (bool) {
return _policySetups[ipId][policyId].inheritedPolicy;
}
/// Returns true if the child is derivative from the parent, by at least 1 policy.
function isParent(address parentIpId, address childIpId) external view returns (bool) {
return _ipIdParents[childIpId].contains(parentIpId);
}
function parentIpIds(address ipId) external view returns (address[] memory) {
return _ipIdParents[ipId].values();
}
function totalParentsForIpId(address ipId) external view returns (uint256) {
return _ipIdParents[ipId].length();
}
function license(uint256 licenseId) external view returns (Licensing.License memory) {
return _licenses[licenseId];
}
function licensorIpId(uint256 licenseId) external view returns (address) {
return _licenses[licenseId].licensorIpId;
}
/// @notice ERC1155 OpenSea metadata JSON representation of the LNFT parameters
function uri(uint256 id) public view virtual override returns (string memory) {
Licensing.License memory licenseData = _licenses[id];
Licensing.Policy memory pol = policy(licenseData.policyId);
return IPolicyFrameworkManager(pol.policyFramework).policyToJson(pol.data);
}
/// @dev Pre-hook for ERC1155's _update() called on transfers.
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory values
) internal virtual override {
// We are interested in transfers, minting and burning are checked in mintLicense and
// linkIpToParent respectively.
if (from != address(0) && to != address(0)) {
for (uint256 i = 0; i < ids.length; i++) {
// Verify transfer params
Licensing.Policy memory pol = policy(_licenses[ids[i]].policyId);
if (ERC165Checker.supportsInterface(pol.policyFramework, type(ITransferParamVerifier).interfaceId)) {
if (
!ITransferParamVerifier(pol.policyFramework).verifyTransfer(
ids[i],
from,
to,
values[i],
pol.data
)
) {
revert Errors.LicenseRegistry__TransferParamFailed();
}
}
}
}
super._update(from, to, ids, values);
}
function _verifyRegisteredFramework(address policyFramework) internal view {
if (!_registeredFrameworkManagers[policyFramework]) {
revert Errors.LicenseRegistry__FrameworkNotFound();
}
}
/// Adds a policy id to the ipId policy set
/// Will revert if policy set already has policyId
/// @param ipId the IP identifier
/// @param policyId id of the policy data
/// @param inheritedPolicy true if set in linkIpToParent, false otherwise
/// @return index of the policy added to the set
function _addPolicyIdToIp(address ipId, uint256 policyId, bool inheritedPolicy) internal returns (uint256 index) {
EnumerableSet.UintSet storage _pols = _policiesPerIpId[ipId];
if (!_pols.add(policyId)) {
revert Errors.LicenseRegistry__PolicyAlreadySetForIpId();
}
// TODO: check for policy compatibility.
// compatibilityManager.isPolicyCompatible(newPolicy, policiesInIpId);
index = _pols.length() - 1;
PolicySetup storage setup = _policySetups[ipId][policyId];
// This should not happen, but just in case
if (setup.isSet) {
revert Errors.LicenseRegistry__PolicyAlreadySetForIpId();
}
setup.index = index;
setup.isSet = true;
setup.active = true;
setup.inheritedPolicy = inheritedPolicy;
emit PolicyAddedToIpId(msg.sender, ipId, policyId, index, inheritedPolicy);
return index;
}
/// Stores data without repetition, assigning an id to it if new or reusing existing one if already stored
/// @param data raw bytes, abi.encode() a value to be hashed
/// @param _hashToIds storage ref to the mapping of hash -> data id
/// @param existingIds amount of distinct data stored.
/// @return id new sequential id if new data, reused id if not new
/// @return isNew True if a new id was generated, signaling the value was stored in _hashToIds.
/// False if id is reused and data was not stored
function _addIdOrGetExisting(
bytes memory data,
mapping(bytes32 => uint256) storage _hashToIds,
uint256 existingIds
) private returns (uint256 id, bool isNew) {
// We could just use the hash of the policy as id to save some gas, but the UX/DX of having huge random
// numbers for ID is bad enough to justify the cost, plus we have accountability on current number of
// policies.
bytes32 hash = keccak256(data);
id = _hashToIds[hash];
if (id != 0) {
return (id, false);
}
id = existingIds + 1;
_hashToIds[hash] = id;
return (id, true);
}
function _verifyPolicy(Licensing.Policy memory pol) private pure {
if (pol.policyFramework == address(0)) {
revert Errors.LicenseRegistry__PolicyNotFound();
}
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
import { ShortStringOps } from "contracts/utils/ShortStringOps.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { IModule } from "contracts/interfaces/modules/base/IModule.sol";
import { ITaggingModule } from "contracts/interfaces/modules/ITaggingModule.sol";
contract TaggingModule is IModule, ITaggingModule {
using ShortStrings for *;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.AddressSet;
uint256 constant MAX_TAG_PERMISSIONS_AT_ONCE = 300;
string public name = "TaggingModule";
mapping(address => EnumerableSet.Bytes32Set) private _tagsForIpIds;
function setTag(string calldata tag, address ipId) external returns (bool added) {
// TODO: access control
// TODO: emit
emit TagSet(tag, ipId);
return _tagsForIpIds[ipId].add(ShortStringOps.stringToBytes32(tag));
}
function removeTag(string calldata tag, address ipId) external returns (bool removed) {
// TODO: access control
emit TagRemoved(tag, ipId);
return _tagsForIpIds[ipId].remove(ShortStringOps.stringToBytes32(tag));
}
function isTagged(string calldata tag, address ipId) external view returns (bool) {
return _tagsForIpIds[ipId].contains(ShortStringOps.stringToBytes32(tag));
}
function totalTagsForIp(address ipId) external view returns (uint256) {
return _tagsForIpIds[ipId].length();
}
function tagAtIndexForIp(address ipId, uint256 index) external view returns (bytes32) {
// WARNING: tag ordering not guaranteed (since they can be removed)
return _tagsForIpIds[ipId].at(index);
}
function tagStringAtIndexForIp(address ipId, uint256 index) external view returns (string memory) {
// WARNING: tag ordering not guaranteed (since they can be removed)
return ShortString.wrap(_tagsForIpIds[ipId].at(index)).toString();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
// external
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
// contracts
import { IRoyaltyModule } from "contracts/interfaces/modules/royalty/IRoyaltyModule.sol";
import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol";
import { Errors } from "contracts/lib/Errors.sol";
/// @title Story Protocol Royalty Module
/// @notice The Story Protocol royalty module allows to set royalty policies an ipId
/// and pay royalties as a derivative ip.
contract RoyaltyModule is IRoyaltyModule, ReentrancyGuard {
/// @notice Indicates if a royalty policy is whitelisted
mapping(address royaltyPolicy => bool allowed) public isWhitelistedRoyaltyPolicy;
/// @notice Indicates if a royalty token is whitelisted
mapping(address token => bool) public isWhitelistedRoyaltyToken;
/// @notice Indicates the royalty policy for a given ipId
mapping(address ipId => address royaltyPolicy) public royaltyPolicies;
/// @notice Restricts the calls to the governance address
modifier onlyGovernance() {
// TODO: where is governance address defined?
_;
}
/// @notice Restricts the calls to the license module
modifier onlyLicensingModule() {
// TODO: where is license module address defined?
_;
}
/// @notice Whitelist a royalty policy
/// @param _royaltyPolicy The address of the royalty policy
/// @param _allowed Indicates if the royalty policy is whitelisted or not
function whitelistRoyaltyPolicy(address _royaltyPolicy, bool _allowed) external onlyGovernance {
if (_royaltyPolicy == address(0)) revert Errors.RoyaltyModule__ZeroRoyaltyPolicy();
isWhitelistedRoyaltyPolicy[_royaltyPolicy] = _allowed;
emit RoyaltyPolicyWhitelistUpdated(_royaltyPolicy, _allowed);
}
/// @notice Whitelist a royalty token
/// @param _token The token address
/// @param _allowed Indicates if the token is whitelisted or not
function whitelistRoyaltyToken(address _token, bool _allowed) external onlyGovernance {
if (_token == address(0)) revert Errors.RoyaltyModule__ZeroRoyaltyToken();
isWhitelistedRoyaltyToken[_token] = _allowed;
emit RoyaltyTokenWhitelistUpdated(_token, _allowed);
}
// TODO: Ensure this function is called on ipId registration: root and derivatives registrations
// TODO: Ensure that the ipId that is passed in from license cannot be manipulated - given ipId addresses are deterministic
/// @notice Sets the royalty policy for an ipId
/// @param _ipId The ipId
/// @param _royaltyPolicy The address of the royalty policy
/// @param _parentIpIds The parent ipIds
/// @param _data The data to initialize the policy
function setRoyaltyPolicy(
address _ipId,
address _royaltyPolicy,
address[] calldata _parentIpIds,
bytes calldata _data
) external onlyLicensingModule nonReentrant {
if (royaltyPolicies[_ipId] != address(0)) revert Errors.RoyaltyModule__AlreadySetRoyaltyPolicy();
if (!isWhitelistedRoyaltyPolicy[_royaltyPolicy]) revert Errors.RoyaltyModule__NotWhitelistedRoyaltyPolicy();
for (uint32 i = 0; i < _parentIpIds.length; i++) {
if (royaltyPolicies[_parentIpIds[i]] != _royaltyPolicy) revert Errors.RoyaltyModule__IncompatibleRoyaltyPolicy();
}
royaltyPolicies[_ipId] = _royaltyPolicy;
IRoyaltyPolicy(_royaltyPolicy).initPolicy(_ipId, _parentIpIds, _data);
emit RoyaltyPolicySet(_ipId, _royaltyPolicy, _data);
}
/// @notice Allows a sender to to pay royalties on behalf of an ipId
/// @param _receiverIpId The ipId that receives the royalties
/// @param _payerIpId The ipId that pays the royalties
/// @param _token The token to use to pay the royalties
/// @param _amount The amount to pay
function payRoyaltyOnBehalf(address _receiverIpId, address _payerIpId, address _token, uint256 _amount) external nonReentrant {
address royaltyPolicy = royaltyPolicies[_receiverIpId];
if (royaltyPolicy == address(0)) revert Errors.RoyaltyModule__NoRoyaltyPolicySet();
if (!isWhitelistedRoyaltyToken[_token]) revert Errors.RoyaltyModule__NotWhitelistedRoyaltyToken();
// TODO: check how to handle the below with replacement
if (!isWhitelistedRoyaltyPolicy[royaltyPolicy]) revert Errors.RoyaltyModule__NotWhitelistedRoyaltyPolicy();
IRoyaltyPolicy(royaltyPolicy).onRoyaltyPayment(msg.sender, _receiverIpId, _token, _amount);
// TODO: review event vs variable for royalty tracking
emit RoyaltyPaid(_receiverIpId, _payerIpId, msg.sender, _token, _amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev the ERC-165 identifier for this interface is `0x6faff5f1`
interface IERC6551Account {
/**
* @dev Allows the account to receive Ether.
*
* Accounts MUST implement a `receive` function.
*
* Accounts MAY perform arbitrary logic to restrict conditions
* under which Ether can be received.
*/
receive() external payable;
/**
* @dev Returns the identifier of the non-fungible token which owns the account.
*
* The return value of this function MUST be constant - it MUST NOT change over time.
*
* @return chainId The EIP-155 ID of the chain the token exists on
* @return tokenContract The contract address of the token
* @return tokenId The ID of the token
*/
function token()
external
view
returns (uint256 chainId, address tokenContract, uint256 tokenId);
/**
* @dev Returns a value that SHOULD be modified each time the account changes state.
*
* @return The current account state
*/
function state() external view returns (uint256);
/**
* @dev Returns a magic value indicating whether a given signer is authorized to act on behalf
* of the account.
*
* MUST return the bytes4 magic value 0x523e3260 if the given signer is valid.
*
* By default, the holder of the non-fungible token the account is bound to MUST be considered
* a valid signer.
*
* Accounts MAY implement additional authorization logic which invalidates the holder as a
* signer or grants signing permissions to other non-holder accounts.
*
* @param signer The address to check signing authorization for
* @param context Additional data used to determine whether the signer is valid
* @return magicValue Magic value indicating whether the signer is valid
*/
function isValidSigner(address signer, bytes calldata context)
external
view
returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol";
/// @title Interface for IP Account Registry
/// @notice This interface manages the registration and tracking of IP Accounts
interface IIPAssetRegistry is IIPAccountRegistry {
/// @notice Emits when an IP is officially registered into the protocol.
/// @param ipId The canonical identifier for the IP.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @param resolver The address of the resolver linked to the IP.
/// @param provider The address of the metadata provider linked to the IP.
/// @param metadata Canonical metadata that was linked to the IP.
event IPRegistered(
address ipId,
uint256 indexed chainId,
address indexed tokenContract,
uint256 indexed tokenId,
address resolver,
address provider,
bytes metadata
);
/// @notice Emits when an IP resolver is bound to an IP.
/// @param ipId The canonical identifier of the specified IP.
/// @param resolver The address of the new resolver bound to the IP.
event IPResolverSet(address ipId, address resolver);
/// @notice Emits when metadata is set for an IP asset.
/// @param ipId The canonical identifier of the specified IP.
/// @param metadataProvider Address of the metadata provider associated with the IP.
/// @param metadata The canonical metadata in bytes associated with the IP.
event MetadataSet(
address indexed ipId,
address indexed metadataProvider,
bytes metadata
);
/// @notice Upgrades the metadata for an IP asset, migrating to a new provider.
/// @param id The canonical ID of the IP.
/// @param metadataProvider Address of the new metadata provider hosting the data.
/// @param data Canonical metadata to associate with the IP.
function setMetadata(address id, address metadataProvider, bytes calldata data) external;
/// @notice Sets the metadata provider to use for new registrations.
/// @param metadataProvider The address of the new metadata provider to use.
function setMetadataProvider(address metadataProvider) external;
/// @notice Registers an NFT as IP, creating a corresponding IP record.
/// @dev This is only callable by an authorized registration module.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @param resolverAddr The address of the resolver to associate with the IP.
/// @param createAccount Whether to create an IP account in the process.
/// @param metadata Metadata in bytes to associate with the IP.
function register(
uint256 chainId,
address tokenContract,
uint256 tokenId,
address resolverAddr,
bool createAccount,
bytes calldata metadata
) external returns (address);
/// @notice Sets the resolver for an IP based on its canonical ID.
/// @param id The canonical ID of the IP.
/// @param resolverAddr The address of the resolver being set.
function setResolver(address id, address resolverAddr) external;
/// @notice Gets the canonical IP identifier associated with an IP (NFT).
/// @dev This is the same as the address of the IP account bound to the IP.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @return The address of the associated IP account.
function ipId(uint256 chainId, address tokenContract, uint256 tokenId) external view returns (address);
/// @notice Checks whether an IP was registered based on its ID.
/// @param id The canonical identifier for the IP.
/// @return Whether the IP was registered into the protocol.
function isRegistered(address id) external view returns (bool);
/// @notice Gets the resolver bound to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The IP resolver address if registered, else the zero address.
function resolver(address id) external view returns (address);
/// @notice Gets the metadata provider currently used for metadata storage.
function metadataProvider() external view returns (address);
/// @notice Gets the metadata provider linked to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The metadata provider that was bound to this IP at creation time.
function metadataProvider(address id) external view returns (address);
/// @notice Gets the metadata linked to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The metadata that was bound to this IP at creation time.
function metadata(address id) external view returns (bytes memory);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { IERC6551Registry } from "lib/reference/src/interfaces/IERC6551Registry.sol";
import { Errors } from "contracts/lib/Errors.sol";
/// @title IPAccountRegistry
/// @notice This contract is responsible for managing the registration and tracking of IP Accounts.
/// It leverages a public ERC6551 registry to deploy IPAccount contracts.
contract IPAccountRegistry is IIPAccountRegistry {
address public immutable IP_ACCOUNT_IMPL;
bytes32 public immutable IP_ACCOUNT_SALT;
address public immutable ERC6551_PUBLIC_REGISTRY;
address public immutable ACCESS_CONTROLLER;
/// @notice Constructor for the IPAccountRegistry contract.
/// @param erc6551Registry_ The address of the ERC6551 registry.
/// @param accessController_ The address of the access controller.
/// @param ipAccountImpl_ The address of the IP account implementation.
constructor(address erc6551Registry_, address accessController_, address ipAccountImpl_) {
if (ipAccountImpl_ == address(0)) revert Errors.IPAccountRegistry_InvalidIpAccountImpl();
IP_ACCOUNT_IMPL = ipAccountImpl_;
IP_ACCOUNT_SALT = bytes32(0);
ERC6551_PUBLIC_REGISTRY = erc6551Registry_;
ACCESS_CONTROLLER = accessController_;
}
/// @notice Deploys an IPAccount contract with the IPAccount implementation and returns the address of the new IP.
/// @param chainId_ The chain ID where the IP Account will be created.
/// @param tokenContract_ The address of the token contract to be associated with the IP Account.
/// @param tokenId_ The ID of the token to be associated with the IP Account.
/// @return ipAccountAddress The address of the newly created IP Account.
function registerIpAccount(
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) public returns (address ipAccountAddress) {
bytes memory initData = abi.encodeWithSignature("initialize(address)", ACCESS_CONTROLLER);
ipAccountAddress = IERC6551Registry(ERC6551_PUBLIC_REGISTRY).createAccount(
IP_ACCOUNT_IMPL,
IP_ACCOUNT_SALT,
chainId_,
tokenContract_,
tokenId_
);
(bool success, bytes memory result) = ipAccountAddress.call(initData);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
emit IPAccountRegistered(ipAccountAddress, IP_ACCOUNT_IMPL, chainId_, tokenContract_, tokenId_);
}
/// @notice Returns the IPAccount address for the given NFT token.
/// @param chainId_ The chain ID where the IP Account is located.
/// @param tokenContract_ The address of the token contract associated with the IP Account.
/// @param tokenId_ The ID of the token associated with the IP Account.
/// @return The address of the IP Account associated with the given NFT token.
function ipAccount(uint256 chainId_, address tokenContract_, uint256 tokenId_) public view returns (address) {
return _get6551AccountAddress(chainId_, tokenContract_, tokenId_);
}
/// @notice Returns the IPAccount implementation address.
/// @return The address of the IPAccount implementation.
function getIPAccountImpl() external view override returns (address) {
return IP_ACCOUNT_IMPL;
}
function _get6551AccountAddress(
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) internal view returns (address) {
return
IERC6551Registry(ERC6551_PUBLIC_REGISTRY).account(
IP_ACCOUNT_IMPL,
IP_ACCOUNT_SALT,
chainId_,
tokenContract_,
tokenId_
);
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol";
/// @title Metadata Provider Interface
interface IMetadataProviderMigratable is IMetadataProvider {
/// @notice Returns the new metadata provider IP assets may migrate to.
/// @return Address of the new metadata provider if set, else the zero address.
function upgradeProvider() external returns(IMetadataProvider);
/// @notice Sets a new metadata provider that IP assets may migrate to.
/// @param provider The address of the new metadata provider.
function setUpgradeProvider(address provider) external;
/// @notice Updates the provider used by the IP asset, migrating existing
/// metadata to the new provider, and adding new metadata.
/// @param ipId The address identifier of the IP asset.
/// @param extraMetadata Additional metadata used by the new metadata provider.
function upgrade(address payable ipId, bytes memory extraMetadata) external;
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IP } from "contracts/lib/IP.sol";
import { MetadataProviderBase } from "./MetadataProviderBase.sol";
import { Errors } from "contracts/lib/Errors.sol";
/// @title IP Metadata Provider v1
/// @notice Storage provider for Story Protocol canonical IP metadata (v1).
contract MetadataProviderV1 is MetadataProviderBase {
/// @notice Initializes the metadata provider contract.
/// @param ipAssetRegistry The protocol-wide IP asset registry.
constructor(address ipAssetRegistry) MetadataProviderBase(ipAssetRegistry) {}
/// @notice Gets the name associated with the IP asset.
/// @param ipId The address identifier of the IP asset.
function name(address ipId) external view returns (string memory) {
return _metadataV1(ipId).name;
}
/// @notice Gets the hash associated with the IP asset.
/// @param ipId The address identifier of the IP asset.
function hash(address ipId) external view returns (bytes32) {
return _metadataV1(ipId).hash;
}
/// @notice Gets the date in which the IP asset was registered.
/// @param ipId The address identifier of the IP asset.
function registrationDate(address ipId) external view returns (uint64) {
return _metadataV1(ipId).registrationDate;
}
/// @notice Gets the initial registrant address of the IP asset.
/// @param ipId The address identifier of the IP asset.
function registrant(address ipId) external view returns (address) {
return _metadataV1(ipId).registrant;
}
/// @notice Gets the external URI associated with the IP asset.
/// @param ipId The address identifier of the IP asset.
function uri(address ipId) external view returns (string memory) {
return _metadataV1(ipId).uri;
}
/// @dev Checks that the data conforms to the canonical metadata standards.
/// @param data The canonical metadata in bytes to verify.
function _verifyMetadata(bytes memory data) internal virtual override {
IP.MetadataV1 memory decodedMetadata = abi.decode(data, (IP.MetadataV1));
if (bytes(decodedMetadata.name).length == 0) {
revert Errors.MetadataProvider__NameInvalid();
}
if (decodedMetadata.hash == "") {
revert Errors.MetadataProvider__HashInvalid();
}
// if (decodedMetadata.registrationDate != uint64(block.timestamp)) {
// revert Errors.MetadataProvider__RegistrationDateInvalid();
// }
if (decodedMetadata.registrant == address(0)) {
revert Errors.MetadataProvider__RegistrantInvalid();
}
if (bytes(decodedMetadata.uri).length == 0) {
revert Errors.MetadataProvider__URIInvalid();
}
}
/// @dev Checks whether two sets of metadata are compatible with one another.
/// TODO: Add try-catch for ABI-decoding error handling.
function _compatible(bytes memory m1, bytes memory m2) internal virtual override pure returns (bool) {
IP.MetadataV1 memory m1Decoded = abi.decode(m1, (IP.MetadataV1));
IP.MetadataV1 memory m2Decoded = abi.decode(m2, (IP.MetadataV1));
return _hash(m1Decoded) == _hash(m2Decoded);
}
/// @dev Gets the bytes32 hash for a MetadataV1 data struct.
function _hash(IP.MetadataV1 memory data) internal pure returns(bytes32) {
return keccak256(
abi.encode(
data.name,
data.hash,
data.registrationDate,
data.registrant,
data.uri
)
);
}
/// @dev Get the decoded canonical metadata belonging to an IP asset.
function _metadataV1(address ipId) internal view returns (IP.MetadataV1 memory) {
return abi.decode(_ipMetadata[ipId], (IP.MetadataV1));
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;
/// @title Errors Library
/// @notice Library for all Story Protocol contract errors.
library Errors {
////////////////////////////////////////////////////////////////////////////
// Governance //
////////////////////////////////////////////////////////////////////////////
error Governance__OnlyProtocolAdmin();
error Governance__ZeroAddress();
error Governance__ProtocolPaused();
error Governance__InconsistentState();
error Governance__NewStateIsTheSameWithOldState();
error Governance__UnsupportedInterface(string interfaceName);
////////////////////////////////////////////////////////////////////////////
// IPAccount //
////////////////////////////////////////////////////////////////////////////
error IPAccount__InvalidSigner();
error IPAccount__InvalidSignature();
error IPAccount__ExpiredSignature();
////////////////////////////////////////////////////////////////////////////
// Module //
////////////////////////////////////////////////////////////////////////////
/// @notice The caller is not allowed to call the provided module.
error Module_Unauthorized();
////////////////////////////////////////////////////////////////////////////
// IPAccountRegistry //
////////////////////////////////////////////////////////////////////////////
error IPAccountRegistry_InvalidIpAccountImpl();
////////////////////////////////////////////////////////////////////////////
// IPAssetRegistry //
////////////////////////////////////////////////////////////////////////////
/// @notice The IP asset has already been registered.
error IPAssetRegistry__AlreadyRegistered();
/// @notice The IP account has already been created.
error IPAssetRegistry__IPAccountAlreadyCreated();
/// @notice The IP asset has not yet been registered.
error IPAssetRegistry__NotYetRegistered();
/// @notice The specified IP resolver is not valid.
error IPAssetRegistry__ResolverInvalid();
/// @notice Caller not authorized to perform the IP registry function call.
error IPAssetRegistry__Unauthorized();
/// @notice The deployed address of account doesn't match with IP ID.
error IPAssetRegistry__InvalidAccount();
/// @notice The metadata provider is not valid.
error IPAssetRegistry__InvalidMetadataProvider();
////////////////////////////////////////////////////////////////////////////
// IPResolver ///
////////////////////////////////////////////////////////////////////////////
/// @notice The targeted IP does not yet have an IP account.
error IPResolver_InvalidIP();
/// @notice Caller not authorized to perform the IP resolver function call.
error IPResolver_Unauthorized();
////////////////////////////////////////////////////////////////////////////
// Metadata Provider ///
////////////////////////////////////////////////////////////////////////////
/// @notice Provided hash metadata is not valid.
error MetadataProvider__HashInvalid();
/// @notice The caller is not the authorized IP asset owner.
error MetadataProvider__IPAssetOwnerInvalid();
/// @notice Provided hash metadata is not valid.
error MetadataProvider__NameInvalid();
/// @notice The new metadata provider is not compatible with the old provider.
error MetadataProvider__MetadataNotCompatible();
/// @notice Provided registrant metadata is not valid.
error MetadataProvider__RegistrantInvalid();
/// @notice Provided registration date is not valid.
error MetadataProvider__RegistrationDateInvalid();
/// @notice Caller does not access to set metadata storage for the provider.
error MetadataProvider__Unauthorized();
/// @notice A metadata provider upgrade is not currently available.
error MetadataProvider__UpgradeUnavailable();
/// @notice The upgrade provider is not valid.
error MetadataProvider__UpgradeProviderInvalid();
/// @notice Provided metadata URI is not valid.
error MetadataProvider__URIInvalid();
////////////////////////////////////////////////////////////////////////////
// LicenseRegistry //
////////////////////////////////////////////////////////////////////////////
error LicenseRegistry__PolicyAlreadySetForIpId();
error LicenseRegistry__FrameworkNotFound();
error LicenseRegistry__EmptyLicenseUrl();
error LicenseRegistry__InvalidPolicyFramework();
error LicenseRegistry__PolicyAlreadyAdded();
error LicenseRegistry__ParamVerifierLengthMismatch();
error LicenseRegistry__PolicyNotFound();
error LicenseRegistry__NotLicensee();
error LicenseRegistry__ParentIdEqualThanChild();
error LicenseRegistry__LicensorDoesntHaveThisPolicy();
error LicenseRegistry__MintLicenseParamFailed();
error LicenseRegistry__LinkParentParamFailed();
error LicenseRegistry__TransferParamFailed();
error LicenseRegistry__InvalidLicensor();
error LicenseRegistry__ParamVerifierAlreadySet();
error LicenseRegistry__CommercialTermInNonCommercialPolicy();
error LicenseRegistry__EmptyParamName();
error LicenseRegistry__UnregisteredFrameworkAddingPolicy();
error LicenseRegistry__UnauthorizedAccess();
error LicenseRegistry__LicensorNotRegistered();
error LicenseRegistry__CallerNotLicensorAndPolicyNotSet();
////////////////////////////////////////////////////////////////////////////
// LicenseRegistryAware //
////////////////////////////////////////////////////////////////////////////
error LicenseRegistryAware__CallerNotLicenseRegistry();
////////////////////////////////////////////////////////////////////////////
// PolicyFrameworkManager //
////////////////////////////////////////////////////////////////////////////
error PolicyFrameworkManager__GettingPolicyWrongFramework();
////////////////////////////////////////////////////////////////////////////
// LicensorApprovalChecker //
////////////////////////////////////////////////////////////////////////////
error LicensorApprovalChecker__Unauthorized();
////////////////////////////////////////////////////////////////////////////
// Dispute Module //
////////////////////////////////////////////////////////////////////////////
error DisputeModule__ZeroArbitrationPolicy();
error DisputeModule__ZeroArbitrationRelayer();
error DisputeModule__ZeroDisputeTag();
error DisputeModule__ZeroLinkToDisputeEvidence();
error DisputeModule__NotWhitelistedArbitrationPolicy();
error DisputeModule__NotWhitelistedDisputeTag();
error DisputeModule__NotWhitelistedArbitrationRelayer();
error DisputeModule__NotDisputeInitiator();
error DisputeModule__NotInDisputeState();
error DisputeModule__NotAbleToResolve();
error ArbitrationPolicySP__ZeroDisputeModule();
error ArbitrationPolicySP__ZeroPaymentToken();
error ArbitrationPolicySP__NotDisputeModule();
////////////////////////////////////////////////////////////////////////////
// Royalty Module //
////////////////////////////////////////////////////////////////////////////
error RoyaltyModule__ZeroRoyaltyPolicy();
error RoyaltyModule__NotWhitelistedRoyaltyPolicy();
error RoyaltyModule__AlreadySetRoyaltyPolicy();
error RoyaltyModule__ZeroRoyaltyToken();
error RoyaltyModule__NotWhitelistedRoyaltyToken();
error RoyaltyModule__NoRoyaltyPolicySet();
error RoyaltyModule__IncompatibleRoyaltyPolicy();
error RoyaltyPolicyLS__ZeroRoyaltyModule();
error RoyaltyPolicyLS__ZeroLiquidSplitFactory();
error RoyaltyPolicyLS__ZeroLiquidSplitMain();
error RoyaltyPolicyLS__NotRoyaltyModule();
error RoyaltyPolicyLS__TransferFailed();
error RoyaltyPolicyLS__InvalidMinRoyalty();
error RoyaltyPolicyLS__InvalidRoyaltyStack();
error RoyaltyPolicyLS__ZeroMinRoyalty();
error RoyaltyPolicyLS__ZeroLicenseRegistry();
error LSClaimer__InvalidPath();
error LSClaimer__InvalidPathFirstPosition();
error LSClaimer__InvalidPathLastPosition();
error LSClaimer__AlreadyClaimed();
error LSClaimer__ZeroRNFT();
error LSClaimer__RNFTAlreadySet();
error LSClaimer__ETHBalanceNotZero();
error LSClaimer__ERC20BalanceNotZero();
error LSClaimer__ZeroIpId();
error LSClaimer__ZeroLicenseRegistry();
error LSClaimer__ZeroRoyaltyPolicyLS();
error LSClaimer__NotRoyaltyPolicyLS();
////////////////////////////////////////////////////////////////////////////
// ModuleRegistry //
////////////////////////////////////////////////////////////////////////////
error ModuleRegistry__ModuleAddressZeroAddress();
error ModuleRegistry__ModuleAddressNotContract();
error ModuleRegistry__ModuleAlreadyRegistered();
error ModuleRegistry__NameEmptyString();
error ModuleRegistry__NameAlreadyRegistered();
error ModuleRegistry__NameDoesNotMatch();
error ModuleRegistry__ModuleNotRegistered();
////////////////////////////////////////////////////////////////////////////
// RegistrationModule //
////////////////////////////////////////////////////////////////////////////
/// @notice The caller is not the owner of the root IP NFT.
error RegistrationModule__InvalidOwner();
////////////////////////////////////////////////////////////////////////////
// AccessController //
////////////////////////////////////////////////////////////////////////////
error AccessController__IPAccountIsZeroAddress();
error AccessController__IPAccountIsNotValid();
error AccessController__SignerIsZeroAddress();
error AccessController__CallerIsNotIPAccount();
error AccessController__PermissionIsNotValid();
////////////////////////////////////////////////////////////////////////////
// TaggingModule //
////////////////////////////////////////////////////////////////////////////
error TaggingModule__InvalidRelationTypeName();
error TaggingModule__RelationTypeAlreadyExists();
error TaggingModule__SrcIpIdDoesNotHaveSrcTag();
error TaggingModule__DstIpIdDoesNotHaveDstTag();
error TaggingModule__RelationTypeDoesNotExist();
}// 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 "contracts/interfaces/modules/base/IModule.sol";
/// @notice Resolver Interface
interface IResolver is IModule {
/// @notice Checks whether the resolver IP interface is supported.
function supportsInterface(bytes4 id) view external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/ERC1155.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "./IERC1155.sol";
import {IERC1155Receiver} from "./IERC1155Receiver.sol";
import {IERC1155MetadataURI} from "./extensions/IERC1155MetadataURI.sol";
import {Context} from "../../utils/Context.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {Arrays} from "../../utils/Arrays.sol";
import {IERC1155Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IERC1155Errors {
using Arrays for uint256[];
using Arrays for address[];
mapping(uint256 id => mapping(address account => uint256)) private _balances;
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
/**
* @dev See {_setURI}.
*/
constructor(string memory uri_) {
_setURI(uri_);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC1155MetadataURI-uri}.
*
* This implementation returns the same URI for *all* token types. It relies
* on the token type ID substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256 /* id */) public view virtual returns (string memory) {
return _uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*/
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] memory accounts,
uint256[] memory ids
) public view virtual returns (uint256[] memory) {
if (accounts.length != ids.length) {
revert ERC1155InvalidArrayLength(ids.length, accounts.length);
}
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts.unsafeMemoryAccess(i), ids.unsafeMemoryAccess(i));
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator) public view virtual returns (bool) {
return _operatorApprovals[account][operator];
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, value, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, values, data);
}
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from`
* (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = _balances[id][from];
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
// Overflow not possible: value <= fromBalance
_balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
_balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, values);
}
}
/**
* @dev Version of {_update} that performs the token acceptance check by calling
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
* contains code (eg. is a smart contract at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
/**
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
* - `ids` and `values` must have the same length.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
* @dev Sets a new URI for all token types, by relying on the token type ID
* substitution mechanism
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the values in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
* interpreted by clients as
* `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
* for token type ID 0x4cce0.
*
* See {uri}.
*
* Because these URIs cannot be meaningfully represented by the {URI} event,
* this function emits no events.
*/
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
/**
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev Destroys a `value` amount of tokens of type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
*/
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
* - `ids` and `values` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-ERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
function _asSingletonArrays(
uint256 element1,
uint256 element2
) private pure returns (uint256[] memory array1, uint256[] memory array2) {
/// @solidity memory-safe-assembly
assembly {
// Load the free memory pointer
array1 := mload(0x40)
// Set array length to 1
mstore(array1, 1)
// Store the single element at the next word after the length (where content starts)
mstore(add(array1, 0x20), element1)
// Repeat for next array locating it right after the first array
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
// Update the free memory pointer by pointing after the second array
mstore(0x40, add(array2, 0x40))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
/// @notice Library for working with Openzeppelin's ShortString data types.
library ShortStringOps {
using ShortStrings for *;
/// @dev Compares whether two ShortStrings are equal.
function equal(ShortString a, ShortString b) internal pure returns (bool) {
return ShortString.unwrap(a) == ShortString.unwrap(b);
}
/// @dev Checks whether a ShortString and a regular string are equal.
function equal(ShortString a, string memory b) internal pure returns (bool) {
return equal(a, b.toShortString());
}
/// @dev Checks whether a regular string and a ShortString are equal.
function equal(string memory a, ShortString b) internal pure returns (bool) {
return equal(a.toShortString(), b);
}
/// @dev Checks whether a bytes32 object and ShortString are equal.
function equal(bytes32 a, ShortString b) internal pure returns (bool) {
return a == ShortString.unwrap(b);
}
/// @dev Checks whether a string and bytes32 object are equal.
function equal(string memory a, bytes32 b) internal pure returns (bool) {
return equal(a, ShortString.wrap(b));
}
/// @dev Checks whether a bytes32 object and string are equal.
function equal(bytes32 a, string memory b) internal pure returns (bool) {
return equal(ShortString.wrap(a), b);
}
function stringToBytes32(string memory s) internal pure returns (bytes32) {
return ShortString.unwrap(s.toShortString());
}
function bytes32ToString(bytes32 b) internal pure returns (string memory) {
return ShortString.wrap(b).toString();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @title IPolicyVerifier
/// @notice Placeholder interface for verifying policy parameters.
interface IPolicyVerifier is IERC165 {
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol";
/// @title IMintParamVerifier
/// @notice LicenseRegistry will call this to verify the minting parameters are compliant
/// with the policy associated with the license to mint.
interface IMintParamVerifier is IPolicyVerifier {
function verifyMint(
address caller,
bool policyWasInherited,
address licensor,
address receiver,
uint256 mintAmount,
bytes memory policyData
) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol";
/// @title ILinkParamVerifier
/// @notice LicenseRegistry will call this to verify the linking an IP to its parent
/// with the policy referenced by the license in use.
interface ILinkParamVerifier is IPolicyVerifier {
function verifyLink(
uint256 licenseId,
address caller,
address ipId,
address parentIpId,
bytes calldata policyData
) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol";
/// @title ITransferParamVerifier
/// @notice LicenseRegistry will call this to verify the transfer parameters are compliant
/// with the policy
interface ITransferParamVerifier is IPolicyVerifier {
function verifyTransfer(
uint256 licenseId,
address from,
address to,
uint256 amount,
bytes memory policyData
) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { Licensing } from "contracts/lib/Licensing.sol";
/// @title ILicenseRegistry
/// @notice Interface for the LicenseRegistry contract, which is the main entry point for the licensing system.
/// It is responsible for:
/// - Registering policy frameworks
/// - Registering policies
/// - Minting licenses
/// - Linking IP to its parent
/// - Verifying transfer parameters (through the ITransferParamVerifier interface implementation by the policy framework)
/// - Verifying linking parameters (through the ILinkParamVerifier interface implementation by the policy framework)
/// - Verifying policy parameters (through the IPolicyVerifier interface implementation by the policy framework)
interface ILicenseRegistry {
/// @notice Emitted when a policy framework is created by registering a policy framework manager
/// @param framework The address of the IPolicyFrameworkManager
/// @param framework The policy framework data
event PolicyFrameworkRegistered(
address indexed framework,
string name,
string licenseTextUrl
);
/// @notice Emitted when a policy is added to the contract
/// @param policyFrameworkManager The address that created the policy
/// @param policyId The id of the policy
/// @param policy The encoded policy data
event PolicyRegistered(
address indexed policyFrameworkManager,
uint256 indexed policyId,
bytes policy
);
/// @notice Emitted when a policy is added to an IP
/// @param caller The address that called the function
/// @param ipId The id of the IP
/// @param policyId The id of the policy
/// @param index The index of the policy in the IP's policy list
/// @param inheritedPolicy Whether the policy was inherited from a parent IP (linking) or set by IP owner
event PolicyAddedToIpId(
address indexed caller,
address indexed ipId,
uint256 indexed policyId,
uint256 index,
bool inheritedPolicy
);
/// @notice Emitted when a license is minted
/// @param creator The address that created the license
/// @param receiver The address that received the license
/// @param licenseId The id of the license
/// @param amount The amount of licenses minted
/// @param licenseData The license data
event LicenseMinted(
address indexed creator,
address indexed receiver,
uint256 indexed licenseId,
uint256 amount,
Licensing.License licenseData
);
/// @notice Emitted when an IP is linked to its parent by burning a license
/// @param caller The address that called the function
/// @param ipId The id of the IP
/// @param parentIpIds The ids of the parent IP
event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] indexed parentIpIds);
/// @notice Registers a policy framework manager into the contract, so it can add policy data for
/// licenses.
/// @param manager the address of the manager. Will be ERC165 checked for IPolicyFrameworkManager
function registerPolicyFrameworkManager(address manager) external;
/// @notice Registers a policy into the contract. MUST be called by a registered
/// framework or it will revert. The policy data and its integrity must be
/// verified by the policy framework manager.
/// @param data The policy data
function registerPolicy(bytes memory data) external returns (uint256 policyId);
/// @notice Adds a policy to an IP policy list
/// @param ipId The id of the IP
/// @param polId The id of the policy
/// @return indexOnIpId The index of the policy in the IP's policy list
function addPolicyToIp(address ipId, uint256 polId) external returns (uint256 indexOnIpId);
/// @notice Mints a license to create derivative IP
/// @param policyId The id of the policy with the licensing parameters
/// @param licensorIpId The id of the licensor IP
/// @param amount The amount of licenses to mint
/// @param receiver The address that will receive the license
function mintLicense(
uint256 policyId,
address licensorIpId,
uint256 amount,
address receiver
) external returns (uint256 licenseId);
/// @notice Links an IP to the licensors (parent IP IDs) listed in the License NFTs, if their policies allow it,
/// burning the NFTs in the proccess. The caller must be the owner of the NFTs and the IP owner.
/// @param licenseIds The id of the licenses to burn
/// @param childIpId The id of the child IP to be linked
/// @param holder The address that holds the license
function linkIpToParents(uint256[] calldata licenseIds, address childIpId, address holder) external;
///
/// Getters
///
/// @notice True if the framework address is registered in LicenseRegistry
function isFrameworkRegistered(address framework) external view returns (bool);
/// @notice Gets total number of policies (framework parameter configurations) in the contract
function totalPolicies() external view returns (uint256);
/// @notice Gets policy data by id
function policy(uint256 policyId) external view returns (Licensing.Policy memory pol);
/// @notice True if policy is defined in the contract
function isPolicyDefined(uint256 policyId) external view returns (bool);
/// @notice Gets the policy ids for an IP
function policyIdsForIp(address ipId) external view returns (uint256[] memory policyIds);
/// @notice Gets total number of policies for an IP
function totalPoliciesForIp(address ipId) external view returns (uint256);
/// @notice True if policy is part of an IP's policy list
function isPolicyIdSetForIp(address ipId, uint256 policyId) external view returns (bool);
/// @notice Gets the policy ID for an IP by index on the IP's policy list
function policyIdForIpAtIndex(address ipId, uint256 index) external view returns (uint256 policyId);
/// @notice Gets the policy for an IP by index on the IP's policy list
function policyForIpAtIndex(address ipId, uint256 index) external view returns (Licensing.Policy memory);
/// @notice Gets the index of a policy in an IP's policy list
function indexOfPolicyForIp(address ipId, uint256 policyId) external view returns (uint256 index);
/// @notice True if the license was added to the IP by linking (burning a license)
function isPolicyInherited(address ipId, uint256 policyId) external view returns (bool);
/// @notice True if holder is the licensee for the license (owner of the license NFT), or derivative IP owner if
/// the license was added to the IP by linking (burning a license)
function isLicensee(uint256 licenseId, address holder) external view returns (bool);
/// @notice IP ID of the licensor for the license (parent IP)
function licensorIpId(uint256 licenseId) external view returns (address);
/// @notice License data (licensor, policy...) for the license id
function license(uint256 licenseId) external view returns (Licensing.License memory);
/// @notice True if an IP is a derivative of another IP
function isParent(address parentIpId, address childIpId) external view returns (bool);
/// @notice Returns the parent IP IDs for an IP ID
function parentIpIds(address ipId) external view returns (address[] memory);
/// @notice Total number of parents for an IP ID
function totalParentsForIpId(address ipId) external view returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import { IPolicyVerifier } from "../interfaces/licensing/IPolicyVerifier.sol";
import { Errors } from "./Errors.sol";
/// @title Licensing
/// @notice Types and constants used by the licensing related contracts
library Licensing {
/// @notice A particular configuration (flavor) of a Policy Framework, setting values for the licensing
/// terms (parameters) of the framework.
/// @param policyFramework address of the IPolicyFrameworkManager this policy is based on
/// @param data Encoded data for the policy, specific to the policy framework
struct Policy {
address policyFramework;
bytes data;
}
/// @notice Data that define a License Agreement NFT
/// @param policyId Id of the policy this license is based on, which will be set in the derivative
/// IP when the license is burnt
/// @param licensorIpId Id of the IP this license is for
struct License {
uint256 policyId;
address licensorIpId;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
import { Licensing } from "contracts/lib/Licensing.sol";
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol";
/// @title IPolicyFrameworkManager
/// @notice Interface to define a policy framework contract, that will
/// register itself into the LicenseRegistry to format policy into the LicenseRegistry
interface IPolicyFrameworkManager is IPolicyVerifier {
// TODO: move here the interfaces for verification and sunset IPolicyVerifier
/// @notice Name to be show in LNFT metadata
function name() external view returns(string memory);
/// @notice URL to the off chain legal agreement template text
function licenseTextUrl() external view returns(string memory);
/// @notice address of Story Protocol license registry
function licenseRegistry() external view returns (address);
/// @notice called by the LicenseRegistry uri(uint256) method.
/// Must return ERC1155 OpenSea standard compliant metadata
function policyToJson(bytes memory policyData) external view returns (string memory);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
interface IAccessController {
event PermissionSet(
address indexed ipAccount,
address indexed signer,
address indexed to,
bytes4 func,
uint8 permission
);
/// @notice Sets the permission for a specific function call
/// @dev Each policy is represented as a mapping from an IP account address to a signer address to a recipient
///// address to a function selector to a permission level.
///// The permission level can be 0 (ABSTAIN), 1 (ALLOW), or 2 (DENY).
/// @param ipAccount_ The account that owns the IP
/// @param signer_ The account that signs the transaction
/// @param to_ The recipient(modules) of the transaction
/// @param func_ The function selector
/// @param permission_ The permission level
function setPermission(address ipAccount_, address signer_, address to_, bytes4 func_, uint8 permission_) external;
/// @notice Sets the permission for all IPAccounts
/// @dev Only the protocol admin can set the global permission
/// @param signer_ The account that signs the transaction
/// @param to_ The recipient(modules) of the transaction
/// @param func_ The function selector
/// @param permission_ The permission level
function setGlobalPermission(address signer_, address to_, bytes4 func_, uint8 permission_) external;
/// @notice Gets the permission for a specific function call
/// @param ipAccount_ The account that owns the IP
/// @param signer_ The account that signs the transaction
/// @param to_ The recipient (modules) of the transaction
/// @param func_ The function selector
/// @return The current permission level for the function call
function getPermission(
address ipAccount_,
address signer_,
address to_,
bytes4 func_
) external view returns (uint8);
/// @notice Checks the permission for a specific function call
/// @dev This function checks the permission level for a specific function call.
/// If a specific permission is set, it overrides the general (wildcard) permission.
/// If the current level permission is ABSTAIN, the final permission is determined by the upper level.
/// @param ipAccount_ The account that owns the IP
/// @param signer_ The account that signs the transaction
/// @param to_ The recipient of the transaction
/// @param func_ The function selector
/// @return A boolean indicating whether the function call is allowed
function checkPermission(
address ipAccount_,
address signer_,
address to_,
bytes4 func_
) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
/// @title Interface for IP Account Registry
/// @notice This interface manages the registration and tracking of IP Accounts
interface IIPAccountRegistry {
/// @notice Event emitted when a new IP Account is created
/// @param account The address of the new IP Account
/// @param implementation The address of the IP Account implementation
/// @param chainId The chain ID where the token contract deployed
/// @param tokenContract The address of the token contract associated with the IP Account
/// @param tokenId The ID of the token associated with the IP Account
event IPAccountRegistered(
address indexed account,
address indexed implementation,
uint256 indexed chainId,
address tokenContract,
uint256 tokenId
);
/// @notice Deploys an IPAccount contract with the IPAccount implementation and returns the address of the new IP
/// @dev The IPAccount deployment deltegates to public ERC6551 Registry
/// @param chainId_ The chain ID where the token contract deployed
/// @param tokenContract_ The address of the token contract to be associated with the IP Account
/// @param tokenId_ The ID of the token to be associated with the IP Account
/// @return The address of the newly created IP Account
function registerIpAccount(
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) external returns (address);
/// @notice Returns the IPAccount address for the given NFT token
/// @param chainId_ The chain ID where the token contract deployed
/// @param tokenContract_ The address of the token contract associated with the IP Account
/// @param tokenId_ The ID of the token associated with the IP Account
/// @return The address of the IP Account associated with the given NFT token
function ipAccount(
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) external view returns (address);
/// @notice Returns the IPAccount implementation address
/// @return The address of the IPAccount implementation
function getIPAccountImpl() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.21;
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { IERC6551Account } from "lib/reference/src/interfaces/IERC6551Account.sol";
import { IIPAccount } from "contracts/interfaces/IIPAccount.sol";
/// @title IPAccountChecker
/// @dev This library provides utility functions to check the registration and validity of IP Accounts.
/// It uses the ERC165 standard for contract introspection and the IIPAccountRegistry interface
/// for account registration checks.
library IPAccountChecker {
/// @notice Returns true if the IPAccount is registered.
/// @param chainId_ The chain ID where the IP Account is located.
/// @param tokenContract_ The address of the token contract associated with the IP Account.
/// @param tokenId_ The ID of the token associated with the IP Account.
/// @return True if the IP Account is registered, false otherwise.
function isRegistered(
IIPAccountRegistry ipAccountRegistry_,
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) external view returns (bool) {
return ipAccountRegistry_.ipAccount(chainId_, tokenContract_, tokenId_).code.length != 0;
}
/// @notice Checks if the given address is a valid IP Account.
/// @param ipAccountRegistry_ The IP Account registry contract.
/// @param ipAccountAddress_ The address to check.
/// @return True if the address is a valid IP Account, false otherwise.
function isIpAccount(
IIPAccountRegistry ipAccountRegistry_,
address ipAccountAddress_
) external view returns (bool) {
if (ipAccountAddress_ == address(0)) return false;
if (ipAccountAddress_.code.length == 0) return false;
if (!ERC165Checker.supportsERC165(ipAccountAddress_)) return false;
if (!ERC165Checker.supportsInterface(ipAccountAddress_, type(IERC6551Account).interfaceId)) return false;
if (!ERC165Checker.supportsInterface(ipAccountAddress_, type(IIPAccount).interfaceId)) return false;
(uint chainId, address tokenContract, uint tokenId) = IIPAccount(payable(ipAccountAddress_)).token();
return ipAccountAddress_ == ipAccountRegistry_.ipAccount(chainId, tokenContract, tokenId);
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
/// @notice Module Interface
interface IModule {
/// @notice Returns the string identifier associated with the module.
function name() external returns (string memory);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
interface ITaggingModule {
event TagSet(string tag, address ipId);
event TagRemoved(string tag, address ipId);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
/// @title RoyaltyModule interface
interface IRoyaltyModule {
/// @notice Event emitted when a royalty policy is whitelisted
/// @param royaltyPolicy The address of the royalty policy
/// @param allowed Indicates if the royalty policy is whitelisted or not
event RoyaltyPolicyWhitelistUpdated(address royaltyPolicy, bool allowed);
/// @notice Event emitted when a royalty token is whitelisted
/// @param token The address of the royalty token
/// @param allowed Indicates if the royalty token is whitelisted or not
event RoyaltyTokenWhitelistUpdated(address token, bool allowed);
/// @notice Event emitted when a royalty policy is set
/// @param ipId The ipId
/// @param royaltyPolicy The address of the royalty policy
/// @param data The data to initialize the policy
event RoyaltyPolicySet(address ipId, address royaltyPolicy, bytes data);
/// @notice Event emitted when royalties are paid
/// @param receiverIpId The ipId that receives the royalties
/// @param payerIpId The ipId that pays the royalties
/// @param sender The address that pays the royalties on behalf of the payer ipId
/// @param token The token that is used to pay the royalties
/// @param amount The amount that is paid
event RoyaltyPaid(address receiverIpId, address payerIpId, address sender, address token, uint256 amount);
/// @notice Whitelist a royalty policy
/// @param royaltyPolicy The address of the royalty policy
/// @param allowed Indicates if the royalty policy is whitelisted or not
function whitelistRoyaltyPolicy(address royaltyPolicy, bool allowed) external;
/// @notice Whitelist a royalty token
/// @param token The token address
/// @param allowed Indicates if the token is whitelisted or not
function whitelistRoyaltyToken(address token, bool allowed) external;
/// @notice Sets the royalty policy for an ipId
/// @param ipId The ipId
/// @param royaltyPolicy The address of the royalty policy
/// @param parentIpIds The parent ipIds
/// @param data The data to initialize the policy
function setRoyaltyPolicy(address ipId, address royaltyPolicy, address[] calldata parentIpIds, bytes calldata data) external;
/// @notice Allows a sender to to pay royalties on behalf of an ipId
/// @param receiverIpId The ipId that receives the royalties
/// @param payerIpId The ipId that pays the royalties
/// @param token The token to use to pay the royalties
/// @param amount The amount to pay
function payRoyaltyOnBehalf(address receiverIpId, address payerIpId, address token, uint256 amount) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
/// @title RoyaltyPolicy interface
interface IRoyaltyPolicy {
/// @notice Initializes the royalty policy
/// @param ipId The ipId
/// @param parentsIpIds The parent ipIds
/// @param data The data to initialize the policy
function initPolicy(address ipId, address[] calldata parentsIpIds, bytes calldata data) external;
/// @notice Allows to pay a royalty
/// @param caller The caller
/// @param ipId The ipId
/// @param token The token to pay
/// @param amount The amount to pay
function onRoyaltyPayment(address caller, address ipId, address token, uint256 amount) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC6551Registry {
/**
* @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
*/
event ERC6551AccountCreated(
address account,
address indexed implementation,
bytes32 salt,
uint256 chainId,
address indexed tokenContract,
uint256 indexed tokenId
);
/**
* @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
*/
error AccountCreationFailed();
/**
* @dev Creates a token bound account for a non-fungible token.
*
* If account has already been created, returns the account address without calling create2.
*
* Emits ERC6551AccountCreated event.
*
* @return account The address of the token bound account
*/
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address account);
/**
* @dev Returns the computed token bound account address for a non-fungible token.
*
* @return account The address of the token bound account
*/
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address account);
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
import { IP } from "contracts/lib/IP.sol";
import { IIPAccount } from "contracts/interfaces/IIPAccount.sol";
import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol";
import { IMetadataProviderMigratable } from "contracts/interfaces/registries/metadata/IMetadataProviderMigratable.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol";
/// @title IP Metadata Provider Base Contract
/// @notice Metadata provider base contract for storing canonical IP metadata.
abstract contract MetadataProviderBase is IMetadataProviderMigratable {
/// @notice Gets the protocol-wide IP asset registry.
IPAssetRegistry public immutable IP_ASSET_REGISTRY;
/// @notice Returns the new metadata provider users may migrate to.
IMetadataProvider public upgradeProvider;
/// @notice Maps IP assets (via their IP ID) to their canonical metadata.
mapping(address ip => bytes metadata) internal _ipMetadata;
/// @notice Restricts calls to only originate from a protocol-authorized caller.
modifier onlyIPAssetRegistry {
if (msg.sender != address(IP_ASSET_REGISTRY)) {
revert Errors.MetadataProvider__Unauthorized();
}
_;
}
/// @notice Initializes the metadata provider contract.
/// @param ipAssetRegistry The protocol-wide IP asset registry.
constructor(address ipAssetRegistry) {
IP_ASSET_REGISTRY = IPAssetRegistry(ipAssetRegistry);
}
/// @notice Gets the IP metadata associated with an IP asset based on its IP ID.
/// @param ipId The IP id of the target IP asset.
function getMetadata(address ipId) external view virtual override returns (bytes memory) {
return _ipMetadata[ipId];
}
/// @notice Sets a upgrade provider for users to migrate their metadata to.
/// @param provider The address of the new metadata provider to migrate to.
function setUpgradeProvider(address provider) external onlyIPAssetRegistry {
if (provider == address(0)) {
revert Errors.MetadataProvider__UpgradeProviderInvalid();
}
// TODO: We may want to add interface detection here if auth changes.
upgradeProvider = IMetadataProviderMigratable(provider);
}
/// @notice Upgrades the metadata provider of an IP asset.
/// @param ipId The IP id of the target IP asset.
/// @param metadata The existing metadata paired with new metadata to add.
function upgrade(address payable ipId, bytes memory metadata) external override {
if (address(upgradeProvider) == address(0)) {
revert Errors.MetadataProvider__UpgradeUnavailable();
}
// TODO: Provide more flexible IPAsset authorization via access controller.
if (msg.sender != IIPAccount(ipId).owner()) {
revert Errors.MetadataProvider__IPAssetOwnerInvalid();
}
if (!_compatible(_ipMetadata[ipId], metadata)) {
revert Errors.MetadataProvider__MetadataNotCompatible();
}
IP_ASSET_REGISTRY.setMetadata(ipId, address(upgradeProvider), metadata);
}
/// @notice Sets the IP metadata associated with an IP asset based on its IP ID.
/// @param ipId The IP id of the IP asset to set metadata for.
/// @param data The metadata in bytes to set for the IP asset.
function setMetadata(address ipId, bytes memory data) external virtual onlyIPAssetRegistry {
_verifyMetadata(data);
_ipMetadata[ipId] = data;
emit MetadataSet(ipId, data);
}
/// @dev Checks that the data conforms to the canonical metadata standards.
/// @param data The canonical metadata in bytes to verify.
function _verifyMetadata(bytes memory data) internal virtual;
/// @dev Checks whether two sets of metadata are compatible with one another.
/// @param m1 The first set of bytes metadata being compared.
/// @param m2 The second set of bytes metadata being compared.
function _compatible(bytes memory m1, bytes memory m2) internal virtual pure returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.20;
import {IERC1155} from "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
// We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
// following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";{
"remappings": [
"@openzeppelin/=node_modules/@openzeppelin/",
"base64-sol/=node_modules/base64-sol/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"openzeppelin-contracts/=lib/reference/lib/openzeppelin-contracts/",
"reference/=lib/reference/"
],
"optimizer": {
"enabled": true,
"runs": 20000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {
"contracts/lib/registries/IPAccountChecker.sol": {
"IPAccountChecker": "0x4687d14d30ea46a60499c2dcc07a56d2d1590fc3"
}
}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"assetRegistry","type":"address"},{"internalType":"address","name":"licenseRegistry","type":"address"},{"internalType":"address","name":"taggingModule","type":"address"},{"internalType":"address","name":"royaltyModule","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"inputs":[],"name":"IP_ASSET_REGISTRY","outputs":[{"internalType":"contract IPAssetRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LICENSE_REGISTRY","outputs":[{"internalType":"contract LicenseRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_MODULE","outputs":[{"internalType":"contract RoyaltyModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TAGGING_MODULE","outputs":[{"internalType":"contract TaggingModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"hash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"registrant","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"registrationDate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ipId","type":"address"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b506040516200162e3803806200162e833981016040819052620000359162000075565b6001600160a01b0393841660805291831660a052821660c0521660e052620000d2565b80516001600160a01b03811681146200007057600080fd5b919050565b600080600080608085870312156200008c57600080fd5b620000978562000058565b9350620000a76020860162000058565b9250620000b76040860162000058565b9150620000c76060860162000058565b905092959194509250565b60805160a05160c05160e05161151b6200011360003960006101b3015260006101070152600061024d0152600081816101660152610541015261151b6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c806373b7ce2811610081578063a2911fcd1161005b578063a2911fcd14610209578063c29fab6d14610235578063f0ebdc831461024857600080fd5b806373b7ce28146101ae5780637e809973146101d557806393702f33146101f657600080fd5b806341c782c5116100b257806341c782c514610161578063426eb01714610188578063666e1b391461019b57600080fd5b806301984892146100d957806304490285146101025780631b562aa51461014e575b600080fd5b6100ec6100e7366004610bee565b61026f565b6040516100f99190610c36565b60405180910390f35b6101297f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100ec61015c366004610bee565b610284565b6101297f000000000000000000000000000000000000000000000000000000000000000081565b6100ec610196366004610bee565b6102df565b6101296101a9366004610bee565b6102f7565b6101297f000000000000000000000000000000000000000000000000000000000000000081565b6101e86101e3366004610bee565b61036e565b6040519081526020016100f9565b6100ec610204366004610bee565b610385565b61021c610217366004610bee565b6104a0565b60405167ffffffffffffffff90911681526020016100f9565b610129610243366004610bee565b6104b7565b6101297f000000000000000000000000000000000000000000000000000000000000000081565b6060600061027c836104ce565b519392505050565b60606000610291836104ce565b805190915061029f8461068d565b6102b06102ab866102f7565b61068d565b83608001516040516020016102c89493929190610ca3565b604051602081830303815290604052915050919050565b606060006102ec836104ce565b608001519392505050565b60008173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103689190610daf565b92915050565b60008061037a836104ce565b602001519392505050565b60606000610392836104ce565b9050600061039f8461068d565b6103a885610284565b6040516020016103b9929190610dcc565b6040516020818303038152906040529050600082600001516103dd6102ab876102f7565b610402856060015173ffffffffffffffffffffffffffffffffffffffff1660146106ac565b602080870151610411916106ac565b610428876040015167ffffffffffffffff166108d6565b60405160200161043c959493929190610e77565b604051602081830303815290604052905061047782826040516020016104639291906110e9565b604051602081830303815290604052610994565b6040516020016104879190611140565b6040516020818303038152906040529350505050919050565b6000806104ac836104ce565b604001519392505050565b6000806104c3836104ce565b606001519392505050565b6040805160a0810182526060808252600060208301819052828401819052818301819052608083019190915291517f015c158000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529192917f0000000000000000000000000000000000000000000000000000000000000000169063015c158090602401602060405180830381865afa158015610588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ac9190610daf565b6040517f2a50c14600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152919250600091831690632a50c14690602401600060405180830381865afa15801561061e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610664919081019061126f565b90508051600014610686578080602001905181019061068391906112f0565b92505b5050919050565b606061036873ffffffffffffffffffffffffffffffffffffffff831660145b60608260006106bc8460026113dc565b6106c79060026113f3565b67ffffffffffffffff8111156106df576106df611185565b6040519080825280601f01601f191660200182016040528015610709576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061074057610740611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106107a3576107a3611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006107df8560026113dc565b6107ea9060016113f3565b90505b6001811115610887577f303132333435363738396162636465660000000000000000000000000000000083600f166010811061082b5761082b611406565b1a60f81b82828151811061084157610841611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049290921c9161088081611435565b90506107ed565b5081156108ce576040517fe22e27eb000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260440160405180910390fd5b949350505050565b606060006108e383610ae7565b600101905060008167ffffffffffffffff81111561090357610903611185565b6040519080825280601f01601f19166020018201604052801561092d576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461093757509392505050565b606081516000036109b357505060408051602081019091526000815290565b60006040518060600160405280604081526020016114a660409139905060006003845160026109e291906113f3565b6109ec919061146a565b6109f79060046113dc565b67ffffffffffffffff811115610a0f57610a0f611185565b6040519080825280601f01601f191660200182016040528015610a39576020820181803683370190505b509050600182016020820185865187015b80821015610aa5576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250610a4a565b5050600386510660018114610ac15760028114610ad457610adc565b603d6001830353603d6002830353610adc565b603d60018303535b509195945050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610b30577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310610b5c576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310610b7a57662386f26fc10000830492506010015b6305f5e1008310610b92576305f5e100830492506008015b6127108310610ba657612710830492506004015b60648310610bb8576064830492506002015b600a83106103685760010192915050565b73ffffffffffffffffffffffffffffffffffffffff81168114610beb57600080fd5b50565b600060208284031215610c0057600080fd5b8135610c0b81610bc9565b9392505050565b60005b83811015610c2d578181015183820152602001610c15565b50506000910152565b6020815260008251806020840152610c55816040850160208701610c12565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60008151610c99818560208601610c12565b9290920192915050565b60008551610cb5818460208a01610c12565b7f2c204950202300000000000000000000000000000000000000000000000000009083019081528551610cef816006840160208a01610c12565b7f2c2069732063757272656e746c79206f776e6564206279000000000000000000600692909101918201528451610d2d81601d840160208901610c12565b7f2e20546f206c6561726e206d6f72652061626f757420746869732049502c2076601d92909101918201527f6973697420000000000000000000000000000000000000000000000000000000603d8201528351610d91816042840160208801610c12565b016042019695505050505050565b8051610daa81610bc9565b919050565b600060208284031215610dc157600080fd5b8151610c0b81610bc9565b7f7b226e616d65223a202249502041737365742023000000000000000000000000815260008351610e04816014850160208801610c12565b7f222c20226465736372697074696f6e223a2022000000000000000000000000006014918401918201528351610e41816027840160208801610c12565b7f222c202261747472696275746573223a205b000000000000000000000000000060279290910191820152603901949350505050565b7f7b2274726169745f74797065223a20224e616d65222c202276616c7565223a2081527f2200000000000000000000000000000000000000000000000000000000000000602082015260008651610ed5816021850160208b01610c12565b7f227d2c7b2274726169745f74797065223a20224f776e6572222c202276616c756021918401918201527f65223a202200000000000000000000000000000000000000000000000000000060418201528651610f38816046840160208b01610c12565b7f227d2c7b2274726169745f74797065223a202252656769737472616e74222c20604692909101918201527f2276616c7565223a20220000000000000000000000000000000000000000000060668201528551610f9c816070840160208a01610c12565b6110dc6110b36110ad61105e61103561102f610fe06070888a01017f227d2c0000000000000000000000000000000000000000000000000000000000815260030190565b7f7b2274726169745f74797065223a202248617368222c202276616c7565223a2081527f2200000000000000000000000000000000000000000000000000000000000000602082015260210190565b8b610c87565b7f227d2c0000000000000000000000000000000000000000000000000000000000815260030190565b7f7b2274726169745f74797065223a2022526567697374726174696f6e2044617481527f65222c202276616c7565223a20220000000000000000000000000000000000006020820152602e0190565b87610c87565b7f227d000000000000000000000000000000000000000000000000000000000000815260020190565b9998505050505050505050565b600083516110fb818460208801610c12565b83519083019061110f818360208801610c12565b7f5d7d0000000000000000000000000000000000000000000000000000000000009101908152600201949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161117881601d850160208701610c12565b91909101601d0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156111d7576111d7611185565b60405290565b600067ffffffffffffffff808411156111f8576111f8611185565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561123e5761123e611185565b8160405280935085815286868601111561125757600080fd5b611265866020830187610c12565b5050509392505050565b60006020828403121561128157600080fd5b815167ffffffffffffffff81111561129857600080fd5b8201601f810184136112a957600080fd5b6108ce848251602084016111dd565b600082601f8301126112c957600080fd5b610c0b838351602085016111dd565b805167ffffffffffffffff81168114610daa57600080fd5b60006020828403121561130257600080fd5b815167ffffffffffffffff8082111561131a57600080fd5b9083019060a0828603121561132e57600080fd5b6113366111b4565b82518281111561134557600080fd5b611351878286016112b8565b8252506020830151602082015261136a604084016112d8565b604082015261137b60608401610d9f565b606082015260808301518281111561139257600080fd5b61139e878286016112b8565b60808301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610368576103686113ad565b80820180821115610368576103686113ad565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081611444576114446113ad565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6000826114a0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f23fb1b9fa34ee1b957aac769d9e14cd28f355fcd0d61af52b0e0a423b383cb64736f6c634300081700330000000000000000000000004c8df7d36cafab8d33016141b025b6521fefe56b00000000000000000000000072a934f9432a214624730a95f6c0ef8af3e944e00000000000000000000000009fbdfab80ed6eb0c5e4b8319c53d924a7b47968d0000000000000000000000006105ce2257ca5a9ba00b774905364007f75a221e
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100d45760003560e01c806373b7ce2811610081578063a2911fcd1161005b578063a2911fcd14610209578063c29fab6d14610235578063f0ebdc831461024857600080fd5b806373b7ce28146101ae5780637e809973146101d557806393702f33146101f657600080fd5b806341c782c5116100b257806341c782c514610161578063426eb01714610188578063666e1b391461019b57600080fd5b806301984892146100d957806304490285146101025780631b562aa51461014e575b600080fd5b6100ec6100e7366004610bee565b61026f565b6040516100f99190610c36565b60405180910390f35b6101297f0000000000000000000000009fbdfab80ed6eb0c5e4b8319c53d924a7b47968d81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100ec61015c366004610bee565b610284565b6101297f0000000000000000000000004c8df7d36cafab8d33016141b025b6521fefe56b81565b6100ec610196366004610bee565b6102df565b6101296101a9366004610bee565b6102f7565b6101297f0000000000000000000000006105ce2257ca5a9ba00b774905364007f75a221e81565b6101e86101e3366004610bee565b61036e565b6040519081526020016100f9565b6100ec610204366004610bee565b610385565b61021c610217366004610bee565b6104a0565b60405167ffffffffffffffff90911681526020016100f9565b610129610243366004610bee565b6104b7565b6101297f00000000000000000000000072a934f9432a214624730a95f6c0ef8af3e944e081565b6060600061027c836104ce565b519392505050565b60606000610291836104ce565b805190915061029f8461068d565b6102b06102ab866102f7565b61068d565b83608001516040516020016102c89493929190610ca3565b604051602081830303815290604052915050919050565b606060006102ec836104ce565b608001519392505050565b60008173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103689190610daf565b92915050565b60008061037a836104ce565b602001519392505050565b60606000610392836104ce565b9050600061039f8461068d565b6103a885610284565b6040516020016103b9929190610dcc565b6040516020818303038152906040529050600082600001516103dd6102ab876102f7565b610402856060015173ffffffffffffffffffffffffffffffffffffffff1660146106ac565b602080870151610411916106ac565b610428876040015167ffffffffffffffff166108d6565b60405160200161043c959493929190610e77565b604051602081830303815290604052905061047782826040516020016104639291906110e9565b604051602081830303815290604052610994565b6040516020016104879190611140565b6040516020818303038152906040529350505050919050565b6000806104ac836104ce565b604001519392505050565b6000806104c3836104ce565b606001519392505050565b6040805160a0810182526060808252600060208301819052828401819052818301819052608083019190915291517f015c158000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529192917f0000000000000000000000004c8df7d36cafab8d33016141b025b6521fefe56b169063015c158090602401602060405180830381865afa158015610588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ac9190610daf565b6040517f2a50c14600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152919250600091831690632a50c14690602401600060405180830381865afa15801561061e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610664919081019061126f565b90508051600014610686578080602001905181019061068391906112f0565b92505b5050919050565b606061036873ffffffffffffffffffffffffffffffffffffffff831660145b60608260006106bc8460026113dc565b6106c79060026113f3565b67ffffffffffffffff8111156106df576106df611185565b6040519080825280601f01601f191660200182016040528015610709576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061074057610740611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106107a3576107a3611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006107df8560026113dc565b6107ea9060016113f3565b90505b6001811115610887577f303132333435363738396162636465660000000000000000000000000000000083600f166010811061082b5761082b611406565b1a60f81b82828151811061084157610841611406565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049290921c9161088081611435565b90506107ed565b5081156108ce576040517fe22e27eb000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260440160405180910390fd5b949350505050565b606060006108e383610ae7565b600101905060008167ffffffffffffffff81111561090357610903611185565b6040519080825280601f01601f19166020018201604052801561092d576020820181803683370190505b5090508181016020015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461093757509392505050565b606081516000036109b357505060408051602081019091526000815290565b60006040518060600160405280604081526020016114a660409139905060006003845160026109e291906113f3565b6109ec919061146a565b6109f79060046113dc565b67ffffffffffffffff811115610a0f57610a0f611185565b6040519080825280601f01601f191660200182016040528015610a39576020820181803683370190505b509050600182016020820185865187015b80821015610aa5576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250610a4a565b5050600386510660018114610ac15760028114610ad457610adc565b603d6001830353603d6002830353610adc565b603d60018303535b509195945050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610b30577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310610b5c576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310610b7a57662386f26fc10000830492506010015b6305f5e1008310610b92576305f5e100830492506008015b6127108310610ba657612710830492506004015b60648310610bb8576064830492506002015b600a83106103685760010192915050565b73ffffffffffffffffffffffffffffffffffffffff81168114610beb57600080fd5b50565b600060208284031215610c0057600080fd5b8135610c0b81610bc9565b9392505050565b60005b83811015610c2d578181015183820152602001610c15565b50506000910152565b6020815260008251806020840152610c55816040850160208701610c12565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60008151610c99818560208601610c12565b9290920192915050565b60008551610cb5818460208a01610c12565b7f2c204950202300000000000000000000000000000000000000000000000000009083019081528551610cef816006840160208a01610c12565b7f2c2069732063757272656e746c79206f776e6564206279000000000000000000600692909101918201528451610d2d81601d840160208901610c12565b7f2e20546f206c6561726e206d6f72652061626f757420746869732049502c2076601d92909101918201527f6973697420000000000000000000000000000000000000000000000000000000603d8201528351610d91816042840160208801610c12565b016042019695505050505050565b8051610daa81610bc9565b919050565b600060208284031215610dc157600080fd5b8151610c0b81610bc9565b7f7b226e616d65223a202249502041737365742023000000000000000000000000815260008351610e04816014850160208801610c12565b7f222c20226465736372697074696f6e223a2022000000000000000000000000006014918401918201528351610e41816027840160208801610c12565b7f222c202261747472696275746573223a205b000000000000000000000000000060279290910191820152603901949350505050565b7f7b2274726169745f74797065223a20224e616d65222c202276616c7565223a2081527f2200000000000000000000000000000000000000000000000000000000000000602082015260008651610ed5816021850160208b01610c12565b7f227d2c7b2274726169745f74797065223a20224f776e6572222c202276616c756021918401918201527f65223a202200000000000000000000000000000000000000000000000000000060418201528651610f38816046840160208b01610c12565b7f227d2c7b2274726169745f74797065223a202252656769737472616e74222c20604692909101918201527f2276616c7565223a20220000000000000000000000000000000000000000000060668201528551610f9c816070840160208a01610c12565b6110dc6110b36110ad61105e61103561102f610fe06070888a01017f227d2c0000000000000000000000000000000000000000000000000000000000815260030190565b7f7b2274726169745f74797065223a202248617368222c202276616c7565223a2081527f2200000000000000000000000000000000000000000000000000000000000000602082015260210190565b8b610c87565b7f227d2c0000000000000000000000000000000000000000000000000000000000815260030190565b7f7b2274726169745f74797065223a2022526567697374726174696f6e2044617481527f65222c202276616c7565223a20220000000000000000000000000000000000006020820152602e0190565b87610c87565b7f227d000000000000000000000000000000000000000000000000000000000000815260020190565b9998505050505050505050565b600083516110fb818460208801610c12565b83519083019061110f818360208801610c12565b7f5d7d0000000000000000000000000000000000000000000000000000000000009101908152600201949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161117881601d850160208701610c12565b91909101601d0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156111d7576111d7611185565b60405290565b600067ffffffffffffffff808411156111f8576111f8611185565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561123e5761123e611185565b8160405280935085815286868601111561125757600080fd5b611265866020830187610c12565b5050509392505050565b60006020828403121561128157600080fd5b815167ffffffffffffffff81111561129857600080fd5b8201601f810184136112a957600080fd5b6108ce848251602084016111dd565b600082601f8301126112c957600080fd5b610c0b838351602085016111dd565b805167ffffffffffffffff81168114610daa57600080fd5b60006020828403121561130257600080fd5b815167ffffffffffffffff8082111561131a57600080fd5b9083019060a0828603121561132e57600080fd5b6113366111b4565b82518281111561134557600080fd5b611351878286016112b8565b8252506020830151602082015261136a604084016112d8565b604082015261137b60608401610d9f565b606082015260808301518281111561139257600080fd5b61139e878286016112b8565b60808301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610368576103686113ad565b80820180821115610368576103686113ad565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081611444576114446113ad565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6000826114a0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f23fb1b9fa34ee1b957aac769d9e14cd28f355fcd0d61af52b0e0a423b383cb64736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004c8df7d36cafab8d33016141b025b6521fefe56b00000000000000000000000072a934f9432a214624730a95f6c0ef8af3e944e00000000000000000000000009fbdfab80ed6eb0c5e4b8319c53d924a7b47968d0000000000000000000000006105ce2257ca5a9ba00b774905364007f75a221e
-----Decoded View---------------
Arg [0] : assetRegistry (address): 0x4c8DF7D36Cafab8D33016141B025b6521FEFe56b
Arg [1] : licenseRegistry (address): 0x72a934f9432A214624730a95F6C0eF8Af3E944E0
Arg [2] : taggingModule (address): 0x9FbDFaB80ED6eb0c5E4b8319c53D924a7b47968D
Arg [3] : royaltyModule (address): 0x6105ce2257CA5a9ba00B774905364007F75a221E
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000004c8df7d36cafab8d33016141b025b6521fefe56b
Arg [1] : 00000000000000000000000072a934f9432a214624730a95f6c0ef8af3e944e0
Arg [2] : 0000000000000000000000009fbdfab80ed6eb0c5e4b8319c53d924a7b47968d
Arg [3] : 0000000000000000000000006105ce2257ca5a9ba00b774905364007f75a221e
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.