Sepolia Testnet

Contract

0x4c8DF7D36Cafab8D33016141B025b6521FEFe56b

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
Amount
0x60a0604052013862024-02-02 2:43:36654 days ago1706841816
0x4c8DF7D3...21FEFe56b
 Contract Creation0 ETH
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:
IPAssetRegistry

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 { 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: 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 { 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;

/// @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
// 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 10 of 20 : 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/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);
}

File 13 of 20 : 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: 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: 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);
}

File 18 of 20 : 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 { 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);
}

File 20 of 20 : 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);
}

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":"accessController","type":"address"},{"internalType":"address","name":"erc6551Registry","type":"address"},{"internalType":"address","name":"ipAccountImpl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"IPAccountRegistry_InvalidIpAccountImpl","type":"error"},{"inputs":[],"name":"IPAssetRegistry__AlreadyRegistered","type":"error"},{"inputs":[],"name":"IPAssetRegistry__InvalidAccount","type":"error"},{"inputs":[],"name":"IPAssetRegistry__NotYetRegistered","type":"error"},{"inputs":[],"name":"IPAssetRegistry__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"},{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"IPAccountRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ipId","type":"address"},{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"tokenContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"resolver","type":"address"},{"indexed":false,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"IPRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ipId","type":"address"},{"indexed":false,"internalType":"address","name":"resolver","type":"address"}],"name":"IPResolverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ipId","type":"address"},{"indexed":true,"internalType":"address","name":"metadataProvider","type":"address"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"MetadataSet","type":"event"},{"inputs":[],"name":"ACCESS_CONTROLLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC6551_PUBLIC_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IP_ACCOUNT_IMPL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IP_ACCOUNT_SALT","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getIPAccountImpl","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"tokenContract_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"ipAccount","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ipId","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"}],"name":"isRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"}],"name":"metadata","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"}],"name":"metadataProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"resolverAddr","type":"address"},{"internalType":"bool","name":"createAccount","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"register","outputs":[{"internalType":"address","name":"id","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"tokenContract_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"registerIpAccount","outputs":[{"internalType":"address","name":"ipAccountAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMetadataProvider","type":"address"}],"name":"setMetadataProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"},{"internalType":"address","name":"resolverAddr","type":"address"}],"name":"setResolver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

610100604052600080553480156200001657600080fd5b506040516200297a3803806200297a833981016040819052620000399162000126565b8183826001600160a01b0381166200006457604051638f54663f60e01b815260040160405180910390fd5b6001600160a01b03908116608052600060a05291821660c0521660e052600180546001600160a01b031916331790556040513090620000a390620000fb565b6001600160a01b039091168152602001604051809103906000f080158015620000d0573d6000803e3d6000fd5b50600380546001600160a01b0319166001600160a01b03929092169190911790555062000170915050565b6111ac80620017ce83390190565b80516001600160a01b03811681146200012157600080fd5b919050565b6000806000606084860312156200013c57600080fd5b620001478462000109565b9250620001576020850162000109565b9150620001676040850162000109565b90509250925092565b60805160a05160c05160e0516115e5620001e96000396000818161021b01526109bd0152600081816101b601528181610b020152610f040152600081816101f401528181610ac30152610ec5015260008181610262015281816102d401528181610a9b01528181610c180152610e9d01526115e56000f3fe608060405234801561001057600080fd5b50600436106101365760003560e01c806364b09402116100b25780639787b61811610081578063d26bb83f11610066578063d26bb83f14610382578063d3d72d2a14610395578063db4fdb31146103b357600080fd5b80639787b618146102f8578063c3c5a5471461033457600080fd5b806364b0940214610299578063689c275a146102ac57806387020f95146102bf57806395a3dfb2146102d257600080fd5b80631a8bb07b116101095780632ba21572116100ee5780632ba215721461023d5780635796ffbc1461025d578063597e99cb1461028457600080fd5b80631a8bb07b146101ef5780631b8b10731461021657600080fd5b8063015c15801461013b57806306d61f3d1461019e5780630c3c7264146101b157806318160ddd146101d8575b600080fd5b61017461014936600461110b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600260205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6101746101ac366004611171565b6103c6565b6101747f000000000000000000000000000000000000000000000000000000000000000081565b6101e160005481565b604051908152602001610195565b6101e17f000000000000000000000000000000000000000000000000000000000000000081565b6101747f000000000000000000000000000000000000000000000000000000000000000081565b61025061024b36600461110b565b610582565b6040516101959190611229565b6101747f000000000000000000000000000000000000000000000000000000000000000081565b61029761029236600461127a565b6106af565b005b6102976102a736600461110b565b6107f1565b6102976102ba3660046112b3565b61090f565b6101746102cd366004611318565b610981565b7f0000000000000000000000000000000000000000000000000000000000000000610174565b61017461030636600461110b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600260205260409020600101541690565b61037261034236600461110b565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526002602052604090206001015416151590565b6040519015158152602001610195565b610174610390366004611318565b610996565b60035473ffffffffffffffffffffffffffffffffffffffff16610174565b6101746103c1366004611318565b6109a3565b60006103d3888888610996565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600260205260409020600101549192501615610438576040517f8325658800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163b15801561045a5750835b801561049c575061046c8888886109a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156104d3576040517f596edc1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104dd8186610c73565b60035461050390829073ffffffffffffffffffffffffffffffffffffffff168585610d31565b60008054908061051283611350565b9091555050600354604051879173ffffffffffffffffffffffffffffffffffffffff808b16928c927fa3028b46ff4aeba585ebfa1c241ad4a453b6f10dc7bc3d3ebaa9cecc680a6f719261056f9288928d9216908b908b906113f8565b60405180910390a4979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff818116600090815260026020526040902054606091166105e3576040517f1b7bf33a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff828116600081815260026020526040908190205490517f2a50c146000000000000000000000000000000000000000000000000000000008152600481019290925290911690632a50c14690602401600060405180830381865afa158015610663573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526106a99190810190611469565b92915050565b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602052604090206001015416610710576040517f1b7bf33a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077f9190611529565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107e3576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107ed8282610c73565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610842576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f9c1bbc0300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015290911690639c1bbc0390602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b5050600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff94909416939093179092555050565b73ffffffffffffffffffffffffffffffffffffffff84811660009081526002602052604090205416331461096f576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097b84848484610d31565b50505050565b600061098e848484610e60565b949350505050565b600061098e848484610981565b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526000908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de800000000000000000000000000000000000000000000000000000000179052517f8a54c52f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000006024830152604482018890528681166064830152608482018690529192507f000000000000000000000000000000000000000000000000000000000000000090911690638a54c52f9060a4016020604051808303816000875af1158015610b4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b719190611529565b91506000808373ffffffffffffffffffffffffffffffffffffffff1683604051610b9b9190611546565b6000604051808303816000865af19150503d8060008114610bd8576040519150601f19603f3d011682016040523d82523d6000602084013e610bdd565b606091505b509150915081610bef57805160208201fd5b6040805173ffffffffffffffffffffffffffffffffffffffff88811682526020820188905289927f0000000000000000000000000000000000000000000000000000000000000000821692918816917f5be70b68c8361762980ec7d425d79fd33f6d49cac8a498e6ddf514f995b987f7910160405180910390a45050509392505050565b610c9d817f01ffc9a700000000000000000000000000000000000000000000000000000000610f71565b5073ffffffffffffffffffffffffffffffffffffffff82811660008181526002602090815260409182902060010180547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905581519283528201929092527f45d82cad942fa2e4cfe12a8e0c0b8ecb938b1c95496042dc75f0ecf94cc794f2910160405180910390a15050565b73ffffffffffffffffffffffffffffffffffffffff8481166000908152600260205260409081902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169286169283179055517f9fd8b5fa000000000000000000000000000000000000000000000000000000008152639fd8b5fa90610dc190879086908690600401611562565b600060405180830381600087803b158015610ddb57600080fd5b505af1158015610def573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f9c8e2e143101fc3123c702e45f91bf26ceec5c14066c4577b98299d3c7ef61f78484604051610e5292919061159b565b60405180910390a350505050565b6040517f246a002100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000006024830152604482018590528381166064830152608482018390526000917f00000000000000000000000000000000000000000000000000000000000000009091169063246a00219060a401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098e9190611529565b6000610f7c83610f94565b8015610f8d5750610f8d8383610ff8565b9392505050565b6000610fc0827f01ffc9a700000000000000000000000000000000000000000000000000000000610ff8565b80156106a95750610ff1827fffffffff00000000000000000000000000000000000000000000000000000000610ff8565b1592915050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000821660248201526000908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825192935060009283928392909183918a617530fa92503d915060005190508280156110cf575060208210155b80156110db5750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461110857600080fd5b50565b60006020828403121561111d57600080fd5b8135610f8d816110e6565b60008083601f84011261113a57600080fd5b50813567ffffffffffffffff81111561115257600080fd5b60208301915083602082850101111561116a57600080fd5b9250929050565b600080600080600080600060c0888a03121561118c57600080fd5b87359650602088013561119e816110e6565b95506040880135945060608801356111b5816110e6565b9350608088013580151581146111ca57600080fd5b925060a088013567ffffffffffffffff8111156111e657600080fd5b6111f28a828b01611128565b989b979a50959850939692959293505050565b60005b83811015611220578181015183820152602001611208565b50506000910152565b6020815260008251806020840152611248816040850160208701611205565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000806040838503121561128d57600080fd5b8235611298816110e6565b915060208301356112a8816110e6565b809150509250929050565b600080600080606085870312156112c957600080fd5b84356112d4816110e6565b935060208501356112e4816110e6565b9250604085013567ffffffffffffffff81111561130057600080fd5b61130c87828801611128565b95989497509550505050565b60008060006060848603121561132d57600080fd5b83359250602084013561133f816110e6565b929592945050506040919091013590565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113a8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff80881683528087166020840152808616604084015250608060608301526110db6080830184866113af565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561147b57600080fd5b815167ffffffffffffffff8082111561149357600080fd5b818401915084601f8301126114a757600080fd5b8151818111156114b9576114b961143a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156114ff576114ff61143a565b8160405282815287602084870101111561151857600080fd5b6110db836020830160208801611205565b60006020828403121561153b57600080fd5b8151610f8d816110e6565b60008251611558818460208701611205565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015260006115926040830184866113af565b95945050505050565b60208152600061098e6020830184866113af56fea26469706673582212201fd527ecf2928fa0f7b4b50e63747920075cab3a57235922516aafb9dd2a92a564736f6c6343000817003360a060405234801561001057600080fd5b506040516111ac3803806111ac83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161110c6100a06000396000818161010f0152818161033101528181610434015261077c015261110c6000f3fe608060405234801561001057600080fd5b50600436106100c95760003560e01c80639c1bbc0311610081578063c29fab6d1161005b578063c29fab6d146101de578063c987336c146101f1578063d091b7c21461020457600080fd5b80639c1bbc031461018a5780639fd8b5fa1461019f578063a2911fcd146101b257600080fd5b806341c782c5116100b257806341c782c51461010a578063426eb017146101565780637e8099731461016957600080fd5b806301984892146100ce5780632a50c146146100f7575b600080fd5b6100e16100dc366004610ac1565b610224565b6040516100ee9190610b53565b60405180910390f35b6100e1610105366004610ac1565b610236565b6101317f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ee565b6100e1610164366004610ac1565b6102ef565b61017c610177366004610ac1565b610304565b6040519081526020016100ee565b61019d610198366004610ac1565b610319565b005b61019d6101ad366004610ca9565b61041c565b6101c56101c0366004610ac1565b610501565b60405167ffffffffffffffff90911681526020016100ee565b6101316101ec366004610ac1565b610516565b61019d6101ff366004610ca9565b61052b565b6000546101319073ffffffffffffffffffffffffffffffffffffffff1681565b606061022f826107ec565b5192915050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260016020526040902080546060919061026a90610cf9565b80601f016020809104026020016040519081016040528092919081815260200182805461029690610cf9565b80156102e35780601f106102b8576101008083540402835291602001916102e3565b820191906000526020600020905b8154815290600101906020018083116102c657829003601f168201915b50505050509050919050565b60606102fa826107ec565b6080015192915050565b600061030f826107ec565b6020015192915050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610388576040517f94b1485700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166103d5576040517f4c80890800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461048b576040517f94b1485700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610494816108e0565b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090206104c38282610d99565b507feb93c52781456784fc831a72052881602fbd4b0251d0715f7e0852cdec233d1e82826040516104f5929190610eb3565b60405180910390a15050565b600061050c826107ec565b6040015192915050565b6000610521826107ec565b6060015192915050565b60005473ffffffffffffffffffffffffffffffffffffffff1661057a576040517fe88f0e8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105e99190610efa565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461064d576040517f425ec99900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090208054610706919061068290610cf9565b80601f01602080910402602001604051908101604052809291908181526020018280546106ae90610cf9565b80156106fb5780601f106106d0576101008083540402835291602001916106fb565b820191906000526020600020905b8154815290600101906020018083116106de57829003601f168201915b505050505082610a07565b61073c576040517fbe1f6fe500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000546040517f689c275a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081169263689c275a926107b692879216908690600401610f17565b600060405180830381600087803b1580156107d057600080fd5b505af11580156107e4573d6000803e3d6000fd5b505050505050565b6040805160a081018252606080825260006020830181905292820183905280820192909252608081019190915273ffffffffffffffffffffffffffffffffffffffff82166000908152600160205260409020805461084990610cf9565b80601f016020809104026020016040519081016040528092919081815260200182805461087590610cf9565b80156108c25780601f10610897576101008083540402835291602001916108c2565b820191906000526020600020905b8154815290600101906020018083116108a557829003601f168201915b50505050508060200190518101906108da9190610fb6565b92915050565b6000818060200190518101906108f69190610fb6565b805151909150600003610935576040517f281d076900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060200151600003610973576040517f293bb8c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606081015173ffffffffffffffffffffffffffffffffffffffff166109c4576040517fc3af260700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806080015151600003610a03576040517f439192a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60008083806020019051810190610a1e9190610fb6565b9050600083806020019051810190610a369190610fb6565b9050610a4181610a54565b610a4a83610a54565b1495945050505050565b8051602080830151604080850151606086015160808701519251600096610a7f969095949101611073565b604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610abe57600080fd5b50565b600060208284031215610ad357600080fd5b8135610ade81610a9c565b9392505050565b60005b83811015610b00578181015183820152602001610ae8565b50506000910152565b60008151808452610b21816020860160208601610ae5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610ade6020830184610b09565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715610bb857610bb8610b66565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610c0557610c05610b66565b604052919050565b600067ffffffffffffffff821115610c2757610c27610b66565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112610c6457600080fd5b8135610c77610c7282610c0d565b610bbe565b818152846020838601011115610c8c57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215610cbc57600080fd5b8235610cc781610a9c565b9150602083013567ffffffffffffffff811115610ce357600080fd5b610cef85828601610c53565b9150509250929050565b600181811c90821680610d0d57607f821691505b602082108103610d46577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115610d94576000816000526020600020601f850160051c81016020861015610d755750805b601f850160051c820191505b818110156107e457828155600101610d81565b505050565b815167ffffffffffffffff811115610db357610db3610b66565b610dc781610dc18454610cf9565b84610d4c565b602080601f831160018114610e1a5760008415610de45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556107e4565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015610e6757888601518255948401946001909101908401610e48565b5085821015610ea357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000610ee26040830184610b09565b949350505050565b8051610ef581610a9c565b919050565b600060208284031215610f0c57600080fd5b8151610ade81610a9c565b600073ffffffffffffffffffffffffffffffffffffffff808616835280851660208401525060606040830152610f506060830184610b09565b95945050505050565b600082601f830112610f6a57600080fd5b8151610f78610c7282610c0d565b818152846020838601011115610f8d57600080fd5b610ee2826020830160208701610ae5565b805167ffffffffffffffff81168114610ef557600080fd5b600060208284031215610fc857600080fd5b815167ffffffffffffffff80821115610fe057600080fd5b9083019060a08286031215610ff457600080fd5b610ffc610b95565b82518281111561100b57600080fd5b61101787828601610f59565b8252506020830151602082015261103060408401610f9e565b604082015261104160608401610eea565b606082015260808301518281111561105857600080fd5b61106487828601610f59565b60808301525095945050505050565b60a08152600061108660a0830188610b09565b86602084015267ffffffffffffffff8616604084015273ffffffffffffffffffffffffffffffffffffffff8516606084015282810360808401526110ca8185610b09565b9897505050505050505056fea2646970667358221220114b38e05040f1206bac623a3e06c0ac49ab2bbba254abe78e27eca0fddc769964736f6c63430008170033000000000000000000000000031400de2e7694898eab1936868e77e88e240de9000000000000000000000000000000006551c19487814612e58fe0681377575800000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101365760003560e01c806364b09402116100b25780639787b61811610081578063d26bb83f11610066578063d26bb83f14610382578063d3d72d2a14610395578063db4fdb31146103b357600080fd5b80639787b618146102f8578063c3c5a5471461033457600080fd5b806364b0940214610299578063689c275a146102ac57806387020f95146102bf57806395a3dfb2146102d257600080fd5b80631a8bb07b116101095780632ba21572116100ee5780632ba215721461023d5780635796ffbc1461025d578063597e99cb1461028457600080fd5b80631a8bb07b146101ef5780631b8b10731461021657600080fd5b8063015c15801461013b57806306d61f3d1461019e5780630c3c7264146101b157806318160ddd146101d8575b600080fd5b61017461014936600461110b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600260205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6101746101ac366004611171565b6103c6565b6101747f000000000000000000000000000000006551c19487814612e58fe0681377575881565b6101e160005481565b604051908152602001610195565b6101e17f000000000000000000000000000000000000000000000000000000000000000081565b6101747f000000000000000000000000031400de2e7694898eab1936868e77e88e240de981565b61025061024b36600461110b565b610582565b6040516101959190611229565b6101747f00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c832435420881565b61029761029236600461127a565b6106af565b005b6102976102a736600461110b565b6107f1565b6102976102ba3660046112b3565b61090f565b6101746102cd366004611318565b610981565b7f00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208610174565b61017461030636600461110b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600260205260409020600101541690565b61037261034236600461110b565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526002602052604090206001015416151590565b6040519015158152602001610195565b610174610390366004611318565b610996565b60035473ffffffffffffffffffffffffffffffffffffffff16610174565b6101746103c1366004611318565b6109a3565b60006103d3888888610996565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600260205260409020600101549192501615610438576040517f8325658800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163b15801561045a5750835b801561049c575061046c8888886109a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156104d3576040517f596edc1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104dd8186610c73565b60035461050390829073ffffffffffffffffffffffffffffffffffffffff168585610d31565b60008054908061051283611350565b9091555050600354604051879173ffffffffffffffffffffffffffffffffffffffff808b16928c927fa3028b46ff4aeba585ebfa1c241ad4a453b6f10dc7bc3d3ebaa9cecc680a6f719261056f9288928d9216908b908b906113f8565b60405180910390a4979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff818116600090815260026020526040902054606091166105e3576040517f1b7bf33a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff828116600081815260026020526040908190205490517f2a50c146000000000000000000000000000000000000000000000000000000008152600481019290925290911690632a50c14690602401600060405180830381865afa158015610663573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526106a99190810190611469565b92915050565b73ffffffffffffffffffffffffffffffffffffffff82811660009081526002602052604090206001015416610710576040517f1b7bf33a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077f9190611529565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107e3576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107ed8282610c73565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610842576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546040517f9c1bbc0300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015290911690639c1bbc0390602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b5050600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff94909416939093179092555050565b73ffffffffffffffffffffffffffffffffffffffff84811660009081526002602052604090205416331461096f576040517faa24098a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097b84848484610d31565b50505050565b600061098e848484610e60565b949350505050565b600061098e848484610981565b60405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000031400de2e7694898eab1936868e77e88e240de91660248201526000908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de800000000000000000000000000000000000000000000000000000000179052517f8a54c52f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208811660048301527f00000000000000000000000000000000000000000000000000000000000000006024830152604482018890528681166064830152608482018690529192507f000000000000000000000000000000006551c19487814612e58fe0681377575890911690638a54c52f9060a4016020604051808303816000875af1158015610b4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b719190611529565b91506000808373ffffffffffffffffffffffffffffffffffffffff1683604051610b9b9190611546565b6000604051808303816000865af19150503d8060008114610bd8576040519150601f19603f3d011682016040523d82523d6000602084013e610bdd565b606091505b509150915081610bef57805160208201fd5b6040805173ffffffffffffffffffffffffffffffffffffffff88811682526020820188905289927f00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208821692918816917f5be70b68c8361762980ec7d425d79fd33f6d49cac8a498e6ddf514f995b987f7910160405180910390a45050509392505050565b610c9d817f01ffc9a700000000000000000000000000000000000000000000000000000000610f71565b5073ffffffffffffffffffffffffffffffffffffffff82811660008181526002602090815260409182902060010180547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905581519283528201929092527f45d82cad942fa2e4cfe12a8e0c0b8ecb938b1c95496042dc75f0ecf94cc794f2910160405180910390a15050565b73ffffffffffffffffffffffffffffffffffffffff8481166000908152600260205260409081902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169286169283179055517f9fd8b5fa000000000000000000000000000000000000000000000000000000008152639fd8b5fa90610dc190879086908690600401611562565b600060405180830381600087803b158015610ddb57600080fd5b505af1158015610def573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f9c8e2e143101fc3123c702e45f91bf26ceec5c14066c4577b98299d3c7ef61f78484604051610e5292919061159b565b60405180910390a350505050565b6040517f246a002100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208811660048301527f00000000000000000000000000000000000000000000000000000000000000006024830152604482018590528381166064830152608482018390526000917f000000000000000000000000000000006551c19487814612e58fe068137757589091169063246a00219060a401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098e9190611529565b6000610f7c83610f94565b8015610f8d5750610f8d8383610ff8565b9392505050565b6000610fc0827f01ffc9a700000000000000000000000000000000000000000000000000000000610ff8565b80156106a95750610ff1827fffffffff00000000000000000000000000000000000000000000000000000000610ff8565b1592915050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000821660248201526000908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825192935060009283928392909183918a617530fa92503d915060005190508280156110cf575060208210155b80156110db5750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461110857600080fd5b50565b60006020828403121561111d57600080fd5b8135610f8d816110e6565b60008083601f84011261113a57600080fd5b50813567ffffffffffffffff81111561115257600080fd5b60208301915083602082850101111561116a57600080fd5b9250929050565b600080600080600080600060c0888a03121561118c57600080fd5b87359650602088013561119e816110e6565b95506040880135945060608801356111b5816110e6565b9350608088013580151581146111ca57600080fd5b925060a088013567ffffffffffffffff8111156111e657600080fd5b6111f28a828b01611128565b989b979a50959850939692959293505050565b60005b83811015611220578181015183820152602001611208565b50506000910152565b6020815260008251806020840152611248816040850160208701611205565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000806040838503121561128d57600080fd5b8235611298816110e6565b915060208301356112a8816110e6565b809150509250929050565b600080600080606085870312156112c957600080fd5b84356112d4816110e6565b935060208501356112e4816110e6565b9250604085013567ffffffffffffffff81111561130057600080fd5b61130c87828801611128565b95989497509550505050565b60008060006060848603121561132d57600080fd5b83359250602084013561133f816110e6565b929592945050506040919091013590565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113a8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff80881683528087166020840152808616604084015250608060608301526110db6080830184866113af565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561147b57600080fd5b815167ffffffffffffffff8082111561149357600080fd5b818401915084601f8301126114a757600080fd5b8151818111156114b9576114b961143a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156114ff576114ff61143a565b8160405282815287602084870101111561151857600080fd5b6110db836020830160208801611205565b60006020828403121561153b57600080fd5b8151610f8d816110e6565b60008251611558818460208701611205565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015260006115926040830184866113af565b95945050505050565b60208152600061098e6020830184866113af56fea26469706673582212201fd527ecf2928fa0f7b4b50e63747920075cab3a57235922516aafb9dd2a92a564736f6c63430008170033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000031400de2e7694898eab1936868e77e88e240de9000000000000000000000000000000006551c19487814612e58fe0681377575800000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208

-----Decoded View---------------
Arg [0] : accessController (address): 0x031400de2e7694898eAB1936868e77E88e240DE9
Arg [1] : erc6551Registry (address): 0x000000006551c19487814612e58FE06813775758
Arg [2] : ipAccountImpl (address): 0x34aA510361a47B1eC6bcd4cF8a480c8324354208

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000031400de2e7694898eab1936868e77e88e240de9
Arg [1] : 000000000000000000000000000000006551c19487814612e58fe06813775758
Arg [2] : 00000000000000000000000034aa510361a47b1ec6bcd4cf8a480c8324354208


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
0x4c8DF7D36Cafab8D33016141B025b6521FEFe56b
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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.