Sepolia Testnet

Contract

0x83E9BaEd1F1058957FeBE7456bB5944C96bB660A

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

Please try again later

Advanced mode:
Parent Transaction Hash Method Block
From
To
Amount
View All Internal Transactions
Loading...
Loading

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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;
    }
}

File 4 of 53 : IP.sol
// 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);
        }
    }
}

File 13 of 53 : IERC721Receiver.sol
// 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));
    }
}

File 22 of 53 : Errors.sol
// 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();
    }
}

File 28 of 53 : IPolicyVerifier.sol
// 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);
}

File 33 of 53 : Licensing.sol
// 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);
    }
}

File 38 of 53 : IModule.sol
// 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);
}

File 39 of 53 : ITaggingModule.sol
// 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
        }
    }
}

File 53 of 53 : IERC165.sol
// 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";

Settings
{
  "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"}]

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


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
0x83E9BaEd1F1058957FeBE7456bB5944C96bB660A
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.