账户
0x00...549b
0x00...549b

0x00...549b

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.23+commit.f704f362
语言
Solidity
合同源代码
文件 1 的 19:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
合同源代码
文件 2 的 19:IERC165.sol
// 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);
}
合同源代码
文件 3 的 19:IERC5192.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.23;

interface IERC5192 {
  /// @notice Emitted when the locking status is changed to locked.
  /// @dev If a token is minted and the status is locked, this event should be emitted.
  /// @param tokenId The identifier for a token.
  event Locked(uint256 indexed tokenId);

  /// @notice Emitted when the locking status is changed to unlocked.
  /// @dev If a token is minted and the status is unlocked, this event should be emitted.
  /// @param tokenId The identifier for a token.
  event Unlocked(uint256 indexed tokenId);

  /// @notice Returns the locking status of an Soulbound Token
  /// @dev SBTs assigned to zero address are considered invalid, and queries
  /// about them do throw.
  /// @param tokenId The identifier for an SBT.
  function locked(uint256 tokenId) external view returns (bool);
}
合同源代码
文件 4 的 19:IERC721.sol
// 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);
}
合同源代码
文件 5 的 19:IPatchwork1155Patch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkScoped.sol";

/**
@title Patchwork Protocol 1155 Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork patch standard
*/
interface IPatchwork1155Patch is IPatchworkScoped {
    /// @dev A canonical path to an 1155 patched target
    struct PatchTarget {
        address addr;    // The address of the 1155
        uint256 tokenId; // The tokenId of the 1155
        address account; // The account for the 1155
    }

    /**
    @notice Creates a new token for the owner, representing a patch
    @param to Address of the owner of the patch token
    @param target Path to an 1155 to patch
    @return tokenId ID of the newly minted token
    */
    function mintPatch(address to, PatchTarget memory target) external payable returns (uint256 tokenId);
}

/**
@title Patchwork Protocol Reversible 1155 Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork patch standard with reverse lookup
*/
interface IPatchworkReversible1155Patch is IPatchwork1155Patch {
    /**
    @notice Returns the token ID (if it exists) for an 1155 that may have been patched
    @dev Requires reverse storage enabled
    @param target The 1155 target that was patched
    @return tokenId token ID of the patch
    */
    function getTokenIdByTarget(PatchTarget memory target) external returns (uint256 tokenId);
}
合同源代码
文件 6 的 19:IPatchwork721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./IERC5192.sol";
import "./IPatchworkScoped.sol";

/** 
@title Patchwork Protocol Interface Metadata
@author Runic Labs, Inc
@notice Metadata for IPatchwork721 and related contract interfaces
*/
interface IPatchworkMetadata {
    /**
    @notice Enumeration of possible field data types.
    @dev This defines the various basic data types for the fields.
     */
    enum FieldType {
        BOOLEAN,  ///< A Boolean type (true or false).
        INT8,     ///< An 8-bit signed integer.
        INT16,    ///< A 16-bit signed integer.
        INT32,    ///< A 32-bit signed integer.
        INT64,    ///< A 64-bit signed integer.
        INT128,   ///< A 128-bit signed integer.
        INT256,   ///< A 256-bit signed integer.
        UINT8,    ///< An 8-bit unsigned integer.
        UINT16,   ///< A 16-bit unsigned integer.
        UINT32,   ///< A 32-bit unsigned integer.
        UINT64,   ///< A 64-bit unsigned integer.
        UINT128,  ///< A 128-bit unsigned integer.
        UINT256,  ///< A 256-bit unsigned integer.
        CHAR8,    ///< An 8-character string.
        CHAR16,   ///< A 16-character string.
        CHAR32,   ///< A 32-character string.
        CHAR64,   ///< A 64-character string.
        LITEREF  ///< A Literef reference to a patchwork fragment
    }

    /**
    @notice Struct defining the metadata schema.
    @dev This defines the overall structure of the metadata and contains entries describing each data field.
    */
    struct MetadataSchema {
        uint256 version;                    ///< Version of the metadata schema.
        MetadataSchemaEntry[] entries;      ///< Array of entries in the schema.
    }

    /**
    @notice Struct defining individual entries within the metadata schema.
    @dev Represents each data field in the schema, detailing its properties and type.
    */
    struct MetadataSchemaEntry {
        uint256 id;                        ///< Index or unique identifier of the entry.
        uint256 permissionId;              ///< Permission identifier associated with the entry.
        FieldType fieldType;               ///< Type of field data (from the FieldType enum).
        uint256 fieldCount;                ///< Number of elements of this field (0 = Dynamic Array, 1 = Single, >1 = Static Array)
        FieldVisibility visibility;        ///< Visibility level of the field.
        uint256 slot;                      ///< Starting storage slot, may span multiple slots based on width.
        uint256 offset;                    ///< Offset in bits within the storage slot.
        string key;                        ///< Key or name associated with the field.
    }

    /**
    @notice Enumeration of field visibility options.
    @dev Specifies whether a field is publicly accessible or private.
    */
    enum FieldVisibility {
        PUBLIC,  ///< Field is publicly accessible.
        PRIVATE  ///< Field is private
    }
}

/**
@title Patchwork Protocol 721 Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork metadata standard
*/
interface IPatchwork721 is IPatchworkScoped, IPatchworkMetadata, IERC5192, IERC721 {
    /**
    @notice Emitted when the freeze status is changed to frozen.
    @param tokenId The identifier for a token.
    */
    event Frozen(uint256 indexed tokenId);

    /**
    @notice Emitted when the locking status is changed to not frozen.
    @param tokenId The identifier for a token.
    */
    event Thawed(uint256 indexed tokenId);

    /**
    @notice Emitted when the permissions are changed
    @param to The address the permissions are assigned to
    @param permissions The permissions
    */
    event PermissionChange(address indexed to, uint256 permissions);

    /**
    @notice Emitted when the schema has changed
    @param addr the address of the Patchwork721
    */
    event SchemaChange(address indexed addr);
    
    /**
    @notice Returns the URI of the schema
    @return string the URI of the schema
    */
    function schemaURI() external view returns (string memory);

    /**
    @notice Returns the metadata schema
    @return MetadataSchema the metadata schema
    */
    function schema() external view returns (MetadataSchema memory);

    /**
    @notice Returns the URI of the image associated with the given token ID
    @param tokenId ID of the token
    @return string the image URI
    */
    function imageURI(uint256 tokenId) external view returns (string memory);

    /**
    @notice Sets permissions for a given address
    @param to Address to set permissions for
    @param permissions Permissions value
    */
    function setPermissions(address to, uint256 permissions) external;

    /**
    @notice Stores packed metadata for a given token ID and slot
    @param tokenId ID of the token
    @param slot Slot to store metadata
    @param data Metadata to store
    */
    function storePackedMetadataSlot(uint256 tokenId, uint256 slot, uint256 data) external;

    /**
    @notice Stores packed metadata for a given token ID
    @param tokenId ID of the token
    @param data Metadata to store
    */
    function storePackedMetadata(uint256 tokenId, uint256[] memory data) external;

    /**
    @notice Loads packed metadata for a given token ID and slot
    @param tokenId ID of the token
    @param slot Slot to load metadata from
    @return uint256 the raw slot data as a uint256
    */
    function loadPackedMetadataSlot(uint256 tokenId, uint256 slot) external view returns (uint256);

    /**
    @notice Loads packed metadata for a given token ID
    @param tokenId ID of the token
    @return uint256[] the raw slot data as a uint256 array
    */
    function loadPackedMetadata(uint256 tokenId) external view returns (uint256[] memory);

    /**
    @notice Returns the freeze nonce for a given token ID
    @param tokenId ID of the token
    @return nonce the nonce
    */
    function getFreezeNonce(uint256 tokenId) external view returns (uint256 nonce);

    /**
    @notice Sets the freeze status of a token
    @param tokenId ID of the token
    @param frozen Freeze status to set
    */
    function setFrozen(uint256 tokenId, bool frozen) external;

    /**
    @notice Gets the freeze status of a token (ERC-5192)
    @param tokenId ID of the token
    @return bool true if frozen, false if not
     */
    function frozen(uint256 tokenId) external view returns (bool);

    /**
    @notice Sets the lock status of a token
    @param tokenId ID of the token
    @param locked Lock status to set
    */
    function setLocked(uint256 tokenId, bool locked) external;
}
合同源代码
文件 7 的 19:IPatchworkAccountPatch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkScoped.sol";

/**
@title Patchwork Protocol Account Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork patch standard
*/
interface IPatchworkAccountPatch is IPatchworkScoped {
    /**
    @notice Creates a new token for the owner, representing a patch
    @param owner Address of the owner of the token
    @param target Address of the original account
    @return tokenId ID of the newly minted token
    */
    function mintPatch(address owner, address target) external payable returns (uint256 tokenId);
}

/**
@title Patchwork Protocol Reversible Account Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork account patch standard with reverse lookup
*/
interface IPatchworkReversibleAccountPatch is IPatchworkAccountPatch {
    /**
    @notice Returns the token ID (if it exists) for an NFT that may have been patched
    @dev Requires reverse storage enabled
    @param target Address of the original account
    @return tokenId ID of the newly minted token
    */
    function getTokenIdByTarget(address target) external returns (uint256 tokenId);
}
合同源代码
文件 8 的 19:IPatchworkAssignable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkScoped.sol";

/**
@title Patchwork Protocol Assignable NFT Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork assignment
*/
interface IPatchworkAssignable is IPatchworkScoped {
    
    /// Represents an assignment of a token from an external NFT contract to a token in this contract.
    struct Assignment {
        address tokenAddr;  /// The address of the external NFT contract.
        uint256 tokenId;    /// The ID of the token in the external NFT contract.
    }

    /**
    @notice Assigns a token to another
    @param ourTokenId ID of our token
    @param to Address to assign to
    @param tokenId ID of the token to assign
    */
    function assign(uint256 ourTokenId, address to, uint256 tokenId) external;

    /**
    @notice Checks permissions for assignment
    @param ourTokenId the tokenID to assign
    @param target the address of the target
    @param targetTokenId the tokenID of the target
    @param targetOwner the ownerOf of the target
    @param by the account invoking the assignment to Patchwork Protocol
    @param scopeName the scope name of the contract to assign to
    */
    function allowAssignment(uint256 ourTokenId, address target, uint256 targetTokenId, address targetOwner, address by, string memory scopeName) external view returns (bool);
}
合同源代码
文件 9 的 19:IPatchworkLiteRef.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**
@title Patchwork Protocol LiteRef NFT Interface
@author Runic Labs, Inc
@notice Interface for contracts that have Lite Reference ID support
*/
interface IPatchworkLiteRef {
    /**
    @notice Emitted when a contract redacts a fragment
    @param target the contract which issued the redaction
    @param fragment the fragment that was redacted
    */
    event Redact(address indexed target, address indexed fragment);

    /**
    @notice Emitted when a contract unredacts a fragment
    @param target the contract which revoked the redaction
    @param fragment the fragment that was unredacted
    */
    event Unredact(address indexed target, address indexed fragment);

    /**
    @notice Emitted when a contract registers a fragment
    @param target the contract that registered the fragment
    @param fragment the fragment that was registered
    @param idx the idx of the literef
    */
    event Register(address indexed target, address indexed fragment, uint8 idx);

    /**
    @notice Registers a reference address
    @param addr Address to register
    @return id ID assigned to the address
    */
    function registerReferenceAddress(address addr) external returns (uint8 id);

    /**
    @notice Gets the ID assigned to the address from registration
    @param addr Registered address
    @return id ID assigned to the address
    @return redacted Redacted status
    */
    function getReferenceId(address addr) external view returns (uint8 id, bool redacted);

    /**
    @notice Gets the address assigned to this id
    @param id ID assigned to the address
    @return addr Registered address
    @return redacted Redacted status
    */
    function getReferenceAddress(uint8 id) external view returns (address addr, bool redacted);

    /**
    @notice Redacts a reference address
    @param id ID of the address to redact
    */
    function redactReferenceAddress(uint8 id) external;

    /**
    @notice Unredacts a reference address
    @param id ID of the address to unredact
    */
    function unredactReferenceAddress(uint8 id) external;

    /**
    @notice Returns a lite reference for a given address and token ID
    @param addr Address to get reference for
    @param tokenId ID of the token
    @return liteRef Lite reference
    @return redacted Redacted status
    */
    function getLiteReference(address addr, uint256 tokenId) external view returns (uint64 liteRef, bool redacted);

    /**
    @notice Returns an address and token ID for a given lite reference
    @param liteRef Lite reference to get address and token ID for
    @return addr Address
    @return tokenId Token ID
    */
    function getReferenceAddressAndTokenId(uint64 liteRef) external view returns (address addr, uint256 tokenId);

    /**
    @notice Adds a reference to a token
    @param tokenId ID of the token
    @param liteRef LiteRef to add
    */
    function addReference(uint256 tokenId, uint64 liteRef) external;

    /**
    @notice Adds a reference to a token
    @param tokenId ID of the token
    @param liteRef LiteRef to add
    @param targetMetadataId The metadata ID on the target to assign to
    */
    function addReference(uint256 tokenId, uint64 liteRef, uint256 targetMetadataId) external;

    /**
    @notice Adds multiple references to a token
    @param tokenId ID of the token
    @param liteRefs Array of lite references to add
    */
    function addReferenceBatch(uint256 tokenId, uint64[] calldata liteRefs) external;

    /**
    @notice Adds multiple references to a token
    @param tokenId ID of the token
    @param liteRefs Array of lite references to add
    @param targetMetadataId The metadata ID on the target to assign to
    */
    function addReferenceBatch(uint256 tokenId, uint64[] calldata liteRefs, uint256 targetMetadataId) external;

    /**
    @notice Removes a reference from a token
    @param tokenId ID of the token
    @param liteRef Lite reference to remove
    */
    function removeReference(uint256 tokenId, uint64 liteRef) external;

    /**
    @notice Removes a reference from a token
    @param tokenId ID of the token
    @param liteRef Lite reference to remove
    @param targetMetadataId The metadata ID on the target to unassign from
    */
    function removeReference(uint256 tokenId, uint64 liteRef, uint256 targetMetadataId) external;

    /**
    @notice Loads a reference address and token ID at a given index
    @param ourTokenId ID of the token
    @param idx Index to load from
    @return addr Address
    @return tokenId Token ID
    */
    function loadReferenceAddressAndTokenId(uint256 ourTokenId, uint256 idx) external view returns (address addr, uint256 tokenId);

    /**
    @notice Loads all static references for a given token ID
    @param tokenId ID of the token
    @return addresses Array of addresses
    @return tokenIds Array of token IDs
    */
    function loadAllStaticReferences(uint256 tokenId) external view returns (address[] memory addresses, uint256[] memory tokenIds);

    /**
    @notice Count all dynamic references for a given token ID
    @param tokenId ID of the token
    @return count the number of dynamic references
    */
    function getDynamicReferenceCount(uint256 tokenId) external view returns (uint256 count);

    /**
    @notice Load a page of dynamic references for a given token ID
    @param tokenId ID of the token
    @param offset The starting offset 0-indexed
    @param count The maximum number of references to return
    @return addresses An array of reference addresses
    @return tokenIds An array of reference token IDs
    */
    function loadDynamicReferencePage(uint256 tokenId, uint256 offset, uint256 count) external view returns (address[] memory addresses, uint256[] memory tokenIds);
}
合同源代码
文件 10 的 19:IPatchworkMintable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkScoped.sol";

/**
@title Patchwork Mintable Interface
@author Runic Labs, Inc
*/
interface IPatchworkMintable is IPatchworkScoped {

    /**
    @notice Mint a new token
    @dev Mints a single token to a specified address.
    @param to The address to which the token will be minted.
    @param data Additional data to be passed to the minting process.
    @return tokenId The ID of the minted token.
    */
    function mint(address to, bytes calldata data) external payable returns (uint256 tokenId);
    
    /**
    @notice Mint a batch of new tokens
    @dev Mints multiple tokens to a specified address.
    @param to The address to which the tokens will be minted.
    @param data Additional data to be passed to the minting process.
    @param quantity The number of tokens to mint.
    @return tokenIds An array of the IDs of the minted tokens.
    */
    function mintBatch(address to, bytes calldata data, uint256 quantity) external payable returns (uint256[] memory tokenIds);
}
合同源代码
文件 11 的 19:IPatchworkMultiAssignable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkAssignable.sol";

/**
@title Patchwork Protocol Assignable NFT Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork assignment
*/
interface IPatchworkMultiAssignable is IPatchworkAssignable {

    /**
    @notice Checks if this fragment is assigned to a target
    @param ourTokenId the tokenId of the fragment
    @param target the address of the target
    @param targetTokenId the tokenId of the target
    */
    function isAssignedTo(uint256 ourTokenId, address target, uint256 targetTokenId) external view returns (bool);

    /**
    @notice Unassigns a token
    @param ourTokenId tokenId of our fragment
    */
    function unassign(uint256 ourTokenId, address target, uint256 targetTokenId) external;

    /**
    @notice Counts the number of unique assignments this token has
    @param tokenId tokenId of our fragment
    */
    function getAssignmentCount(uint256 tokenId) external view returns (uint256);

    /**
    @notice Gets assignments for a fragment
    @param tokenId tokenId of our fragment
    @param offset the page offset
    @param count the maximum numer of entries to return
    */
    function getAssignments(uint256 tokenId, uint256 offset, uint256 count) external view returns (Assignment[] memory);
}
合同源代码
文件 12 的 19:IPatchworkPatch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkScoped.sol";

/**
@title Patchwork Protocol Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork patch standard
*/
interface IPatchworkPatch is IPatchworkScoped {
    /// @dev A canonical path to an 721 patched
    struct PatchTarget {
        address addr;    // The address of the 721
        uint256 tokenId; // The tokenId of the 721
    }
    
    /**
    @notice Creates a new token for the owner, representing a patch
    @param owner Address of the owner of the token
    @param target path to target of patch
    @return tokenId ID of the newly minted token
    */
    function mintPatch(address owner, PatchTarget memory target) external payable returns (uint256 tokenId);

    /**
    @notice Updates the real underlying ownership of a token in storage (if different from current)
    @param tokenId ID of the token
    */
    function updateOwnership(uint256 tokenId) external;

    /**
    @notice Returns the underlying stored owner of a token ignoring real patched NFT ownership
    @param tokenId ID of the token
    @return address Address of the owner
    */
    function ownerOfPatch(uint256 tokenId) external view returns (address);
}

/**
@title Patchwork Protocol Reversible Patch Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork patch standard with reverse lookup
*/
interface IPatchworkReversiblePatch is IPatchworkPatch {
    /**
    @notice Returns the token ID (if it exists) for an NFT that may have been patched
    @dev Requires reverse storage enabled
    @param target Patch to target of patch
    @return tokenId token ID of the patch
    */
    function getTokenIdByTarget(PatchTarget memory target) external view returns (uint256 tokenId);
}
合同源代码
文件 13 的 19:IPatchworkProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**
@title Patchwork Protocol Interface
@author Runic Labs, Inc
@notice Interface for Patchwork Protocol
*/
interface IPatchworkProtocol {
    /**
    @notice The address is not authorized to perform this action
    @param addr The address attempting to perform the action
    */
    error NotAuthorized(address addr);

    /**
    @notice The scope with the provided name already exists
    @param scopeName Name of the scope
    */
    error ScopeExists(string scopeName);

    /**
    @notice The scope with the provided name does not exist
    @param scopeName Name of the scope
    */
    error ScopeDoesNotExist(string scopeName);

    /**
    @notice Transfer of the scope to the provided address is not allowed
    @param to Address not allowed for scope transfer
    */
    error ScopeTransferNotAllowed(address to);

    /**
    @notice The token with the provided ID at the given address is frozen
    @param addr Address of the token owner
    @param tokenId ID of the frozen token
    */
    error Frozen(address addr, uint256 tokenId);

    /**
    @notice The token with the provided ID at the given address is locked
    @param addr Address of the token owner
    @param tokenId ID of the locked token
    */
    error Locked(address addr, uint256 tokenId);

    /**
    @notice The address is not whitelisted for the given scope
    @param scopeName Name of the scope
    @param addr Address that isn't whitelisted
    */
    error NotWhitelisted(string scopeName, address addr);

    /**
    @notice The address at the given address has already been patched
    @param addr The address that was patched
    @param patchAddress Address of the patch applied
    */
    error AccountAlreadyPatched(address addr, address patchAddress);

    /**
    @notice The token at the given address has already been patched
    @param addr Address of the original 721
    @param tokenId ID of the patched token
    @param patchAddress Address of the patch applied
    */
    error AlreadyPatched(address addr, uint256 tokenId, address patchAddress);

    /**
    @notice The ERC1155 path has already been patched
    @param addr Address of the 1155
    @param tokenId ID of the patched token
    @param account The account patched
    @param patchAddress Address of the patch applied
    */
    error ERC1155AlreadyPatched(address addr, uint256 tokenId, address account, address patchAddress);

    /**
    @notice The provided input lengths are not compatible or valid
    @dev for any multi array inputs, they must be the same length
    */
    error BadInputLengths();

    /**
    @notice The fragment at the given address is unregistered
    @param addr Address of the unregistered fragment
    */
    error FragmentUnregistered(address addr);

    /**
    @notice The fragment at the given address has been redacted
    @param addr Address of the redacted fragment
    */
    error FragmentRedacted(address addr);

    /**
    @notice The fragment with the provided ID at the given address is already assigned
    @param addr Address of the fragment
    @param tokenId ID of the assigned fragment
    */
    error FragmentAlreadyAssigned(address addr, uint256 tokenId);

    /**
    @notice The reference was not found for the given fragment and target
    @param target Address of the target token
    @param fragment Address of the fragment
    @param tokenId ID of the fragment
    */
    error RefNotFound(address target, address fragment, uint256 tokenId);

    /**
    @notice The fragment with the provided ID at the given address is not assigned
    @param addr Address of the fragment
    @param tokenId ID of the fragment
    */
    error FragmentNotAssigned(address addr, uint256 tokenId);

    /**
    @notice The fragment with the provided ID at the given address is not assigned to the target
    @param addr Address of the fragment
    @param tokenId ID of the fragment
    @param targetAddress Address of the target
    @param targetTokenId ID of the target
    */
    error FragmentNotAssignedToTarget(address addr, uint256 tokenId, address targetAddress, uint256 targetTokenId);

    /**
    @notice The fragment at the given address is already registered
    @param addr Address of the registered fragment
    */
    error FragmentAlreadyRegistered(address addr);

    /**
    @notice Ran out of available IDs for allocation
    @dev Max 255 IDs per target
    */
    error OutOfIDs();

    /**
    @notice The provided token ID is unsupported
    @dev TokenIds may only be 56 bits long
    @param tokenId The unsupported token ID
    */
    error UnsupportedTokenId(uint256 tokenId);

    /**
    @notice Cannot lock the soulbound patch at the given address
    @param addr Address of the soulbound patch
    */
    error CannotLockSoulboundPatch(address addr);

    /**
    @notice The token with the provided ID at the given address is not frozen
    @param addr Address of the token owner
    @param tokenId ID of the token
    */
    error NotFrozen(address addr, uint256 tokenId);

    /**
    @notice The nonce for the token with the provided ID at the given address is incorrect
    @dev It may be incorrect or a newer nonce may be present
    @param addr Address of the token owner
    @param tokenId ID of the token
    @param nonce The incorrect nonce
    */
    error IncorrectNonce(address addr, uint256 tokenId, uint256 nonce);

    /**
    @notice Self assignment of the token with the provided ID at the given address is not allowed
    @param addr Address of the token owner
    @param tokenId ID of the token
    */
    error SelfAssignmentNotAllowed(address addr, uint256 tokenId);

    /**
    @notice Transfer of the token with the provided ID at the given address is not allowed
    @param addr Address of the token owner
    @param tokenId ID of the token
    */
    error TransferNotAllowed(address addr, uint256 tokenId);

    /**
    @notice Transfer of the token with the provided ID at the given address is blocked by an assignment
    @param addr Address of the token owner
    @param tokenId ID of the token
    */
    error TransferBlockedByAssignment(address addr, uint256 tokenId);

    /**
    @notice A rule is blocking the mint to this owner address
    @param addr Address of the token owner
    */
    error MintNotAllowed(address addr);

    /**
    @notice The token at the given address is not IPatchworkAssignable
    @param addr Address of the non-assignable token
    */
    error NotPatchworkAssignable(address addr);

    /**
    @notice A data integrity error has been detected
    @dev Addr+TokenId is expected where addr2+tokenId2 is present
    @param addr Address of the first token
    @param tokenId ID of the first token
    @param addr2 Address of the second token
    @param tokenId2 ID of the second token
    */
    error DataIntegrityError(address addr, uint256 tokenId, address addr2, uint256 tokenId2);

    /**
    @notice The available balance does not satisfy the amount
    */
    error InsufficientFunds();

    /**
    @notice The supplied fee is not the corret amount
    */
    error IncorrectFeeAmount();

    /**
    @notice Minting is not active for this address 
    */
    error MintNotActive();

    /**
    @notice The value could not be sent 
    */
    error FailedToSend();   
    
    /**
    @notice The contract is not supported
    */
    error UnsupportedContract();
    
    /**
    @notice The operation is not supported
    */
    error UnsupportedOperation();

    /**
    @notice No proposed fee is set 
    */
    error NoProposedFeeSet();

    /**
    @notice Timelock has not elapsed
    */
    error TimelockNotElapsed();

    /**
    @notice Invalid fee value 
    */
    error InvalidFeeValue();

    /**
    @notice No delegate proposed 
    */
    error NoDelegateProposed();
    
    /** 
    @notice Fee Configuration
    */
    struct FeeConfig {
        uint256 mintBp;   /// mint basis points (10000 = 100%)
        uint256 patchBp;  /// patch basis points (10000 = 100%)
        uint256 assignBp; /// assign basis points (10000 = 100%)
    }

    /** 
    @notice Fee Configuration Override
    */
    struct FeeConfigOverride {
        uint256 mintBp;   /// mint basis points (10000 = 100%)
        uint256 patchBp;  /// patch basis points (10000 = 100%)
        uint256 assignBp; /// assign basis points (10000 = 100%)
        bool active; /// true for present
    }

    /**
    @notice Proposal to change a fee configuration for either protocol or scope override
    */
    struct ProposedFeeConfig {
        FeeConfig config;
        uint256 timestamp;
        bool active; /// Used to enable/disable overrides - ignored for protocol
    }

    /**
    @notice Mint configuration
    */
    struct MintConfig {
        uint256 flatFee; /// fee per 1 quantity mint in wei
        bool active;     /// If the mint is active
    }

    /**
    @notice Proposed assigner delegate
    */
    struct ProposedAssignerDelegate {
        uint256 timestamp;
        address addr;
    }

    /**
    @notice Represents a defined scope within the system
    @dev Contains details about the scope ownership, permissions, and mappings for references and assignments
    */
    struct Scope {
        /**
        @notice Owner of this scope
        @dev Address of the account or contract that owns this scope
        */
        address owner;

        /**
        @notice Owner-elect
        @dev Used in two-step transfer process. If this is set, only this owner can accept the transfer
        */
        address ownerElect;

        /**
        @notice Indicates whether a user is allowed to patch within this scope
        @dev True if a user can patch, false otherwise. If false, only operators and the scope owner can perform patching.
        */
        bool allowUserPatch;

        /**
        @notice Indicates whether a user is allowed to assign within this scope
        @dev True if a user can assign, false otherwise. If false, only operators and the scope owner can perform assignments.
        */
        bool allowUserAssign;

        /**
        @notice Indicates if a whitelist is required for operations within this scope
        @dev True if whitelist is required, false otherwise
        */
        bool requireWhitelist;

        /**
        @notice Mapped list of operator addresses for this scope
        @dev Address of the operator mapped to a boolean indicating if they are an operator
        */
        mapping(address => bool) operators;

        /**
        @notice Mapped whitelist of addresses that belong to this scope
        @dev Address mapped to a boolean indicating if it's whitelisted
        */
        mapping(address => bool) whitelist;

        /**
        @notice Mapped list of mint configurations for this scope
        @dev Address of the IPatchworkMintable mapped to the configuration
        */
        mapping(address => MintConfig) mintConfigurations;

        /**
        @notice Mapped list of patch fees for this scope
        @dev Address of a 721, 1155 or account patch mapped to the fee in wei 
        */
        mapping(address => uint256) patchFees;

        /**
        @notice Mapped list of assign fees for this scope
        @dev Address of an IPatchworkAssignable mapped to the fee in wei 
        */        
        mapping(address => uint256) assignFees;

        /**
        @notice Balance in wei for this scope
        @dev accrued in mint, patch and assign fees, may only be withdrawn by scope bankers
        */
        uint256 balance;

        /**
        @notice Mapped list of addresses that are designated bankers for this scope 
        @dev Address mapped to a boolean indicating if they are a banker
        */
        mapping(address => bool) bankers;
    }

    /**
    @notice Emitted when a fragment is assigned
    @param owner The owner of the target and fragment
    @param fragmentAddress The address of the fragment's contract
    @param fragmentTokenId The tokenId of the fragment
    @param targetAddress The address of the target's contract
    @param targetTokenId The tokenId of the target
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event Assign(address indexed owner, address fragmentAddress, uint256 fragmentTokenId, address indexed targetAddress, uint256 indexed targetTokenId, uint256 scopeFee, uint256 protocolFee);

    /**
    @notice Emitted when a fragment is unassigned
    @param owner The owner of the fragment
    @param fragmentAddress The address of the fragment's contract
    @param fragmentTokenId The tokenId of the fragment
    @param targetAddress The address of the target's contract
    @param targetTokenId The tokenId of the target
    */
    event Unassign(address indexed owner, address fragmentAddress, uint256 fragmentTokenId, address indexed targetAddress, uint256 indexed targetTokenId);

    /**
    @notice Emitted when a patch is minted
    @param owner The owner of the patch
    @param originalAddress The address of the original 721's contract
    @param originalTokenId The tokenId of the original 721
    @param patchAddress The address of the patch's contract
    @param patchTokenId The tokenId of the patch
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event Patch(address indexed owner, address originalAddress, uint256 originalTokenId, address indexed patchAddress, uint256 indexed patchTokenId, uint256 scopeFee, uint256 protocolFee);

    /**
    @notice Emitted when a patch is minted
    @param owner The owner of the patch
    @param originalAddress The address of the original 1155's contract
    @param originalTokenId The tokenId of the original 1155
    @param originalAccount The address of the original 1155's account
    @param patchAddress The address of the patch's contract
    @param patchTokenId The tokenId of the patch
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event ERC1155Patch(address indexed owner, address originalAddress, uint256 originalTokenId, address originalAccount, address indexed patchAddress, uint256 indexed patchTokenId, uint256 scopeFee, uint256 protocolFee);


    /**
    @notice Emitted when an account patch is minted
    @param owner The owner of the patch
    @param originalAddress The address of the original account
    @param patchAddress The address of the patch's contract
    @param patchTokenId The tokenId of the patch
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event AccountPatch(address indexed owner, address originalAddress, address indexed patchAddress, uint256 indexed patchTokenId, uint256 scopeFee, uint256 protocolFee);

    /**
    @notice Emitted when a new scope is claimed
    @param scopeName The name of the claimed scope
    @param owner The owner of the scope
    */
    event ScopeClaim(string scopeName, address indexed owner);

    /**
    @notice Emitted when a scope has elected a new owner to transfer to
    @param scopeName The name of the transferred scope
    @param from The owner of the scope
    @param to The owner-elect of the scope
    */
    event ScopeTransferElect(string scopeName, address indexed from, address indexed to);

    /**
    @notice Emitted when a scope transfer is canceled
    @param scopeName The name of the transferred scope
    @param from The owner of the scope
    @param to The owner-elect of the scope
    */
    event ScopeTransferCancel(string scopeName, address indexed from, address indexed to);

    /**
    @notice Emitted when a scope is transferred
    @param scopeName The name of the transferred scope
    @param from The address transferring the scope
    @param to The recipient of the scope
    */
    event ScopeTransfer(string scopeName, address indexed from, address indexed to);

    /**
    @notice Emitted when a scope has an operator added
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param operator The new operator's address
    */
    event ScopeAddOperator(string scopeName, address indexed actor, address indexed operator);

    /**
    @notice Emitted when a scope has an operator removed
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param operator The operator's address being removed
    */
    event ScopeRemoveOperator(string scopeName, address indexed actor, address indexed operator);

    /**
    @notice Emitted when a scope's rules are changed
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param allowUserPatch Indicates whether user patches are allowed
    @param allowUserAssign Indicates whether user assignments are allowed
    @param requireWhitelist Indicates whether a whitelist is required
    */
    event ScopeRuleChange(string scopeName, address indexed actor, bool allowUserPatch, bool allowUserAssign, bool requireWhitelist);

    /**
    @notice Emitted when a scope has an address added to the whitelist
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param addr The address being added to the whitelist
    */
    event ScopeWhitelistAdd(string scopeName, address indexed actor, address indexed addr);

    /**
    @notice Emitted when a scope has an address removed from the whitelist
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param addr The address being removed from the whitelist
    */
    event ScopeWhitelistRemove(string scopeName, address indexed actor, address indexed addr);

    /**
    @notice Emitted when a mint is configured
    @param scopeName The name of the scope
    @param mintable The address of the IPatchworkMintable
    @param config The mint configuration
    */
    event MintConfigure(string scopeName, address indexed actor, address indexed mintable, MintConfig config);

    /**
    @notice Emitted when a banker is added to a scope
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param banker The banker that was added
    */
    event ScopeBankerAdd(string scopeName, address indexed actor, address indexed banker);

    /**
    @notice Emitted when a banker is removed from a scope
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param banker The banker that was removed
    */
    event ScopeBankerRemove(string scopeName, address indexed actor, address indexed banker);
    
    /**
    @notice Emitted when a withdrawl is made from a scope
    @param scopeName The name of the scope
    @param actor The address responsible for the action
    @param amount The amount withdrawn
    */    
    event ScopeWithdraw(string scopeName, address indexed actor, uint256 amount);

    /**
    @notice Emitted when a banker is added to the protocol
    @param actor The address responsible for the action
    @param banker The banker that was added
    */
    event ProtocolBankerAdd(address indexed actor, address indexed banker);

    /**
    @notice Emitted when a banker is removed from the protocol
    @param actor The address responsible for the action
    @param banker The banker that was removed
    */
    event ProtocolBankerRemove(address indexed actor, address indexed banker);

    /**
    @notice Emitted when a withdrawl is made from the protocol
    @param actor The address responsible for the action
    @param amount The amount withdrawn
    */
    event ProtocolWithdraw(address indexed actor, uint256 amount);

    /**
    @notice Emitted on mint
    @param actor The address responsible for the action
    @param scopeName The scope of the IPatchworkMintable
    @param to The receipient of the mint
    @param mintable The IPatchworkMintable minted
    @param data The data used to mint
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event Mint(address indexed actor, string scopeName, address indexed to, address indexed mintable, bytes data, uint256 scopeFee, uint256 protocolFee);

    /**
    @notice Emitted on batch mint
    @param actor The address responsible for the action
    @param scopeName The scope of the IPatchworkMintable
    @param to The receipient of the mint
    @param mintable The IPatchworkMintable minted
    @param data The data used to mint
    @param quantity The quantity minted
    @param scopeFee The fee collected to the scope
    @param protocolFee The fee collected to the protocol
    */
    event MintBatch(address indexed actor, string scopeName, address indexed to, address indexed mintable, bytes data, uint256 quantity, uint256 scopeFee, uint256 protocolFee);

    /**
    @notice Emitted on protocol fee config proposed
    @param config The fee configuration
    */
    event ProtocolFeeConfigPropose(FeeConfig config);

    /**
    @notice Emitted on protocol fee config committed
    @param config The fee configuration
    */
    event ProtocolFeeConfigCommit(FeeConfig config);

    /**
    @notice Emitted on scope fee config override proposed
    @param scopeName The scope
    @param config The fee configuration
    */
    event ScopeFeeOverridePropose(string scopeName, FeeConfigOverride config);

    /**
    @notice Emitted on scope fee config override committed
    @param scopeName The scope
    @param config The fee configuration
    */
    event ScopeFeeOverrideCommit(string scopeName, FeeConfigOverride config);

    /**
    @notice Emitted on patch fee change
    @param scopeName The scope of the patch
    @param addr The address of the patch
    @param fee The new fee
    */
    event PatchFeeChange(string scopeName, address indexed addr, uint256 fee);

    /**
    @notice Emitted on assign fee change 
    @param scopeName The scope of the assignable
    @param addr The address of the assignable
    @param fee The new fee
    */
    event AssignFeeChange(string scopeName, address indexed addr, uint256 fee);

    /**
    @notice Emitted on assigner delegate propose
    @param addr The address of the delegate
    */
    event AssignerDelegatePropose(address indexed addr);

    /**
    @notice Emitted on assigner delegate commit
    @param addr The address of the delegate
    */
    event AssignerDelegateCommit(address indexed addr);

    /**
    @notice Claim a scope
    @param scopeName the name of the scope
    */
    function claimScope(string calldata scopeName) external;

    /**
    @notice Transfer ownership of a scope
    @dev must be accepted by transferee - see {acceptScopeTransfer}
    @param scopeName Name of the scope
    @param newOwner Address of the new owner
    */
    function transferScopeOwnership(string calldata scopeName, address newOwner) external;

    /**
    @notice Cancel a pending scope transfer
    @param scopeName Name of the scope
    */
    function cancelScopeTransfer(string calldata scopeName) external;

    /**
    @notice Accept a scope transfer
    @param scopeName Name of the scope
    */
    function acceptScopeTransfer(string calldata scopeName) external;

    /**
    @notice Get owner-elect of a scope
    @param scopeName Name of the scope
    @return ownerElect Address of the scope's owner-elect
    */
    function getScopeOwnerElect(string calldata scopeName) external view returns (address ownerElect);

    /**
    @notice Get owner of a scope
    @param scopeName Name of the scope
    @return owner Address of the scope owner
    */
    function getScopeOwner(string calldata scopeName) external view returns (address owner);

    /**
    @notice Add an operator to a scope
    @param scopeName Name of the scope
    @param op Address of the operator
    */
    function addOperator(string calldata scopeName, address op) external;

    /**
    @notice Remove an operator from a scope
    @param scopeName Name of the scope
    @param op Address of the operator
    */
    function removeOperator(string calldata scopeName, address op) external;

    /**
    @notice Set rules for a scope
    @param scopeName Name of the scope
    @param allowUserPatch Boolean indicating whether user patches are allowed
    @param allowUserAssign Boolean indicating whether user assignments are allowed
    @param requireWhitelist Boolean indicating whether whitelist is required
    */
    function setScopeRules(string calldata scopeName, bool allowUserPatch, bool allowUserAssign, bool requireWhitelist) external;

    /**
    @notice Add an address to a scope's whitelist
    @param scopeName Name of the scope
    @param addr Address to be whitelisted
    */
    function addWhitelist(string calldata scopeName, address addr) external;

    /**
    @notice Remove an address from a scope's whitelist
    @param scopeName Name of the scope
    @param addr Address to be removed from the whitelist
    */
    function removeWhitelist(string calldata scopeName, address addr) external;

    /**
    @notice Set the mint configuration for a given address
    @param addr The address for which to set the mint configuration, must be IPatchworkMintable
    @param config The mint configuration to be set
    */
    function setMintConfiguration(address addr, MintConfig memory config) external;

    /**
    @notice Get the mint configuration for a given address
    @param addr The address for which to get the mint configuration
    @return config The mint configuration of the given address
    */
    function getMintConfiguration(address addr) external view returns (MintConfig memory config);

    /**
    @notice Set the patch fee for a given address
    @dev must be banker of scope claimed by addr to call
    @param addr The address for which to set the patch fee
    @param baseFee The patch fee to be set in wei
    */
    function setPatchFee(address addr, uint256 baseFee) external;

    /**
    @notice Get the patch fee for a given address
    @param addr The address for which to get the patch fee
    @return baseFee The patch fee of the given address in wei
    */
    function getPatchFee(address addr) external view returns (uint256 baseFee);

    /**
    @notice Set the assign fee for a given fragment address
    @dev must be banker of scope claimed by fragmentAddress to call
    @param fragmentAddress The address of the fragment for which to set the fee
    @param baseFee The assign fee to be set in wei
    */
    function setAssignFee(address fragmentAddress, uint256 baseFee) external;

    /**
    @notice Get the assign fee for a given fragment address
    @param fragmentAddress The address of the fragment for which to get the fee
    @return baseFee The assign fee of the given fragment address in wei
    */
    function getAssignFee(address fragmentAddress) external view returns (uint256 baseFee);

    /**
    @notice Add a banker to a given scope
    @dev must be owner of scope to call
    @param scopeName The name of the scope
    @param addr The address to be added as a banker
    */
    function addBanker(string memory scopeName, address addr) external;

    /**
    @notice Remove a banker from a given scope
    @dev must be owner of scope to call
    @param scopeName The name of the scope
    @param addr The address to be removed as a banker
    */
    function removeBanker(string memory scopeName, address addr) external;

    /**
    @notice Withdraw an amount from the balance of a given scope
    @dev must be owner of scope or banker of scope to call
    @dev transfers to the msg.sender
    @param scopeName The name of the scope
    @param amount The amount to be withdrawn in wei
    */
    function withdraw(string memory scopeName, uint256 amount) external;

    /**
    @notice Get the balance of a given scope
    @param scopeName The name of the scope
    @return balance The balance of the given scope in wei
    */
    function balanceOf(string memory scopeName) external view returns (uint256 balance);

    /**
    @notice Mint a new token
    @param to The address to which the token will be minted
    @param mintable The address of the IPatchworkMintable contract
    @param data Additional data to be passed to the minting
    @return tokenId The ID of the minted token
    */
    function mint(address to, address mintable, bytes calldata data) external payable returns (uint256 tokenId);

    /**
    @notice Mint a batch of new tokens
    @param to The address to which the tokens will be minted
    @param mintable The address of the IPatchworkMintable contract
    @param data Additional data to be passed to the minting
    @param quantity The number of tokens to mint
    @return tokenIds An array of the IDs of the minted tokens
    */
    function mintBatch(address to, address mintable, bytes calldata data, uint256 quantity) external payable returns (uint256[] memory tokenIds);

    /**
    @notice Proposes a protocol fee configuration
    @dev must be protocol owner or banker to call
    @dev configuration does not apply until commitProtocolFeeConfig is called
    @param config The protocol fee configuration to be set
    */
    function proposeProtocolFeeConfig(FeeConfig memory config) external;

    /**
    @notice Commits the current proposed protocol fee configuration
    @dev must be protocol owner or banker to call
    @dev may only be called after timelock has passed
    */
    function commitProtocolFeeConfig() external;

    /**
    @notice Get the current protocol fee configuration
    @return config The current protocol fee configuration
    */
    function getProtocolFeeConfig() external view returns (FeeConfig memory config);

    /**
    @notice Proposes a protocol fee override for a scope
    @dev must be protocol owner or banker to call
    @param config The protocol fee override configuration to be set
    */
    function proposeScopeFeeOverride(string memory scopeName, FeeConfigOverride memory config) external;

    /**
    @notice Commits the current proposed protocol fee override configuration for a scope
    @dev must be protocol owner or banker to call
    @dev may only be called after timelock has passed
    */
    function commitScopeFeeOverride(string memory scopeName) external;

    /**
    @notice Get the protocol fee override for a scope
    @return config The current protocol fee override
    */
    function getScopeFeeOverride(string memory scopeName) external view returns (FeeConfigOverride memory config);

    /**
    @notice Add a banker to the protocol
    @dev must be protocol owner to call
    @param addr The address to be added as a protocol banker
    */
    function addProtocolBanker(address addr) external;

    /**
    @notice Remove a banker from the protocol
    @dev must be protocol owner to call
    @param addr The address to be removed as a protocol banker
    */
    function removeProtocolBanker(address addr) external;

    /**
    @notice Withdraw a specified amount from the protocol balance
    @dev must be protocol owner or banker to call
    @dev transfers to the msg.sender
    @param balance The amount to be withdrawn in wei
    */
    function withdrawFromProtocol(uint256 balance) external;

    /**
    @notice Get the current balance of the protocol
    @return balance The balance of the protocol in wei
    */
    function balanceOfProtocol() external view returns (uint256 balance);

    /**
    @notice Create a new patch
    @param owner The owner of the patch
    @param originalAddress Address of the original 721
    @param originalTokenId Token ID of the original 721
    @param patchAddress Address of the IPatchworkPatch to mint
    @return tokenId Token ID of the newly created patch
    */
    function patch(address owner, address originalAddress, uint originalTokenId, address patchAddress) external payable returns (uint256 tokenId);

    /**
    @notice Callback for when a patch is burned
    @dev can only be called from the patchAddress
    @param originalAddress Address of the original 721
    @param originalTokenId Token ID of the original 721
    @param patchAddress Address of the IPatchworkPatch to mint
    */
    function patchBurned(address originalAddress, uint originalTokenId, address patchAddress) external;

    /**
    @notice Create a new 1155 patch
    @param originalAddress Address of the original 1155
    @param originalTokenId Token ID of the original 1155
    @param originalAccount Address of the account to patch
    @param patchAddress Address of the IPatchworkPatch to mint
    @return tokenId Token ID of the newly created patch
    */
    function patch1155(address to, address originalAddress, uint originalTokenId, address originalAccount, address patchAddress) external payable returns (uint256 tokenId);
    
    /**
    @notice Callback for when an 1155 patch is burned
    @dev can only be called from the patchAddress
    @param originalAddress Address of the original 1155
    @param originalTokenId Token ID of the original 1155
    @param originalAccount Address of the account to patch
    @param patchAddress Address of the IPatchworkPatch to mint
    */
    function patchBurned1155(address originalAddress, uint originalTokenId, address originalAccount, address patchAddress) external;

    /**
    @notice Create a new account patch
    @param owner The owner of the patch
    @param originalAddress Address of the original account
    @param patchAddress Address of the IPatchworkPatch to mint
    @return tokenId Token ID of the newly created patch
    */
    function patchAccount(address owner, address originalAddress, address patchAddress) external payable returns (uint256 tokenId);

    /**
    @notice Callback for when an account patch is burned
    @dev can only be called from the patchAddress
    @param originalAddress Address of the original 1155
    @param patchAddress Address of the IPatchworkPatch to mint
    */
    function patchBurnedAccount(address originalAddress, address patchAddress) external;

    /**
    @notice Assigns a relation to have an IPatchworkLiteRef form a LiteRef to a IPatchworkAssignable
    @param fragment The IPatchworkAssignable address to assign
    @param fragmentTokenId The IPatchworkAssignable Token ID to assign
    @param target The IPatchworkLiteRef address to hold the reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID to hold the reference to the fragment
    */
    function assign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) external payable;

    /**
    @notice Assigns a relation to have an IPatchworkLiteRef form a LiteRef to a IPatchworkAssignable
    @param fragment The IPatchworkAssignable address to assign
    @param fragmentTokenId The IPatchworkAssignable Token ID to assign
    @param target The IPatchworkLiteRef address to hold the reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID to hold the reference to the fragment
    @param targetMetadataId The metadata ID on the target to store the reference in
    */
    function assign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) external payable;

    /**
    @notice Assign multiple fragments to a target in batch
    @param fragments The array of addresses of the fragment IPatchworkAssignables
    @param tokenIds The array of token IDs of the fragment IPatchworkAssignables
    @param target The address of the target IPatchworkLiteRef 
    @param targetTokenId The token ID of the target IPatchworkLiteRef 
    */
    function assignBatch(address[] calldata fragments, uint256[] calldata tokenIds, address target, uint256 targetTokenId) external payable;

    /**
    @notice Assign multiple fragments to a target in batch
    @param fragments The array of addresses of the fragment IPatchworkAssignables
    @param tokenIds The array of token IDs of the fragment IPatchworkAssignables
    @param target The address of the target IPatchworkLiteRef 
    @param targetTokenId The token ID of the target IPatchworkLiteRef 
    @param targetMetadataId The metadata ID on the target to store the references in
    */
    function assignBatch(address[] calldata fragments, uint256[] calldata tokenIds, address target, uint256 targetTokenId, uint256 targetMetadataId) external payable;

    /**
    @notice Unassign a fragment from a target
    @param fragment The IPatchworkSingleAssignable address of the fragment
    @param fragmentTokenId The IPatchworkSingleAssignable token ID of the fragment
    @dev reverts if fragment is not an IPatchworkSingleAssignable
    */
    function unassignSingle(address fragment, uint256 fragmentTokenId) external;
    
    /**
    @notice Unassign a fragment from a target
    @param fragment The IPatchworkSingleAssignable address of the fragment
    @param fragmentTokenId The IPatchworkSingleAssignable token ID of the fragment
    @param targetMetadataId The metadata ID on the target to unassign from
    @dev reverts if fragment is not an IPatchworkSingleAssignable
    */
    function unassignSingle(address fragment, uint256 fragmentTokenId, uint256 targetMetadataId) external;

    /**
    @notice Unassigns a multi relation
    @param fragment The IPatchworMultiAssignable address to unassign
    @param fragmentTokenId The IPatchworkMultiAssignable Token ID to unassign
    @param target The IPatchworkLiteRef address which holds a reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID which holds a reference to the fragment
    @dev reverts if fragment is not an IPatchworkMultiAssignable
    */
    function unassignMulti(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) external;

    /**
    @notice Unassigns a multi relation
    @param fragment The IPatchworMultiAssignable address to unassign
    @param fragmentTokenId The IPatchworkMultiAssignable Token ID to unassign
    @param target The IPatchworkLiteRef address which holds a reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID which holds a reference to the fragment
    @param targetMetadataId The metadata ID on the target to unassign from
    @dev reverts if fragment is not an IPatchworkMultiAssignable
    */
    function unassignMulti(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) external;

    /**
    @notice Unassigns a relation (single or multi)
    @param fragment The IPatchworkAssignable address to unassign
    @param fragmentTokenId The IPatchworkAssignable Token ID to unassign
    @param target The IPatchworkLiteRef address which holds a reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID which holds a reference to the fragment
    */
    function unassign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) external;

    /**
    @notice Unassigns a relation (single or multi)
    @param fragment The IPatchworkAssignable address to unassign
    @param fragmentTokenId The IPatchworkAssignable Token ID to unassign
    @param target The IPatchworkLiteRef address which holds a reference to the fragment
    @param targetTokenId The IPatchworkLiteRef Token ID which holds a reference to the fragment
    @param targetMetadataId The metadata ID on the target to unassign from
    */
    function unassign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) external;

    /**
    @notice Apply transfer rules and actions of a specific token from one address to another
    @param from The address of the sender
    @param to The address of the receiver
    @param tokenId The ID of the token to be transferred
    */
    function applyTransfer(address from, address to, uint256 tokenId) external;

    /**
    @notice Update the ownership tree of a specific Patchwork 721
    @param addr The address of the Patchwork 721
    @param tokenId The ID of the token whose ownership tree needs to be updated
    */
    function updateOwnershipTree(address addr, uint256 tokenId) external;

    /**
    @notice Propose an assigner delegate module
    @param addr The address of the new delegate module
    */
    function proposeAssignerDelegate(address addr) external;

    /**
    @notice Commit the proposed assigner delegate module
    @dev must be past timelock
    */
    function commitAssignerDelegate() external;
}
合同源代码
文件 14 的 19:IPatchworkScoped.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**
@title Patchwork Protocol Scoped Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting scopes
*/
interface IPatchworkScoped {
    /**
    @notice Get the scope this NFT claims to belong to
    @return string the name of the scope
    */
    function getScopeName() external view returns (string memory);
}
合同源代码
文件 15 的 19:IPatchworkSingleAssignable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./IPatchworkAssignable.sol";

/**
@title Patchwork Protocol Assignable Interface
@author Runic Labs, Inc
@notice Interface for contracts supporting Patchwork assignment
*/
interface IPatchworkSingleAssignable is IPatchworkAssignable {
    /**
    @notice Unassigns a token
    @param ourTokenId ID of our token
    */
    function unassign(uint256 ourTokenId) external;

    /**
    @notice Returns the address and token ID that our token is assigned to
    @param ourTokenId ID of our token
    @return address the address this is assigned to
    @return uint256 the tokenId this is assigned to
    */
    function getAssignedTo(uint256 ourTokenId) external view returns (address, uint256);

    /**
    @notice Returns the underlying stored owner of a token ignoring current assignment
    @param ourTokenId ID of our token
    @return address address of the owner
    */
    function unassignedOwnerOf(uint256 ourTokenId) external view returns (address);

    /**
    @notice Sends events for a token when the assigned-to token has been transferred
    @param from Sender address
    @param to Recipient address
    @param tokenId ID of the token
    */
    function onAssignedTransfer(address from, address to, uint256 tokenId) external;

    /**
    @notice Updates the real underlying ownership of a token in storage (if different from current)
    @param tokenId ID of the token
    */
    function updateOwnership(uint256 tokenId) external;
}
合同源代码
文件 16 的 19:Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
合同源代码
文件 17 的 19:PatchworkProtocol.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**

    ____        __       __                       __  
   / __ \____ _/ /______/ /_ _      ______  _____/ /__
  / /_/ / __ `/ __/ ___/ __ \ | /| / / __ \/ ___/ //_/
 / ____/ /_/ / /_/ /__/ / / / |/ |/ / /_/ / /  / ,<   
/_/ ___\__,_/\__/\___/_/ /_/|__/|__/\____/_/  /_/|_|  
   / __ \_________  / /_____  _________  / /          
  / /_/ / ___/ __ \/ __/ __ \/ ___/ __ \/ /           
 / ____/ /  / /_/ / /_/ /_/ / /__/ /_/ / /            
/_/   /_/   \____/\__/\____/\___/\____/_/          

*/

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./PatchworkProtocolCommon.sol";
import "./interfaces/IPatchwork721.sol";
import "./interfaces/IPatchworkSingleAssignable.sol";
import "./interfaces/IPatchworkMultiAssignable.sol";
import "./interfaces/IPatchworkLiteRef.sol";
import "./interfaces/IPatchworkPatch.sol";
import "./interfaces/IPatchwork1155Patch.sol";
import "./interfaces/IPatchworkAccountPatch.sol";
import "./interfaces/IPatchworkProtocol.sol";
import "./interfaces/IPatchworkMintable.sol";
import "./interfaces/IPatchworkScoped.sol";

/** 
@title Patchwork Protocol
@author Runic Labs, Inc
*/
contract PatchworkProtocol is IPatchworkProtocol, PatchworkProtocolCommon {
    
    /// How much time must elapse before a fee change can be committed (1209600 = 2 weeks)
    uint256 public constant FEE_CHANGE_TIMELOCK = 1209600; 

    /// How much time must elapse before a contract upgrade can be committed (1209600 = 2 weeks)
    uint256 public constant CONTRACT_UPGRADE_TIMELOCK = 1209600; 

    /// The denominator for fee basis points
    uint256 private constant _FEE_BASIS_DENOM = 10000;

    /// The maximum basis points patchwork can ever be configured to
    uint256 private constant _PROTOCOL_FEE_CEILING = 3000;

    /// Constructor
    /// @param owner_ The address of the initial owner
    constructor(address owner_, address assignerDelegate_) PatchworkProtocolCommon(owner_) {
        _assignerDelegate = assignerDelegate_;
    }

    /**
    @dev See {IPatchworkProtocol-claimScope}
    */
    function claimScope(string calldata scopeName) public {
        if (bytes(scopeName).length == 0) {
            revert NotAuthorized(msg.sender);
        }
        Scope storage s = _scopes[scopeName];
        if (s.owner != address(0)) {
            revert ScopeExists(scopeName);
        }
        s.owner = msg.sender;
        s.requireWhitelist = true; // better security by default
        emit ScopeClaim(scopeName, msg.sender);
    }

    /**
    @dev See {IPatchworkProtocol-transferScopeOwnership}
    */
    function transferScopeOwnership(string calldata scopeName, address newOwner) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwner(s);
        if (newOwner == address(0)) {
            revert ScopeTransferNotAllowed(address(0));
        }
        s.ownerElect = newOwner;
        emit ScopeTransferElect(scopeName, s.owner, s.ownerElect);
    }

    /**
    @dev See {IPatchworkProtocol-cancelScopeTransfer}
    */
    function cancelScopeTransfer(string calldata scopeName) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwner(s);
        emit ScopeTransferCancel(scopeName, s.owner, s.ownerElect);
        s.ownerElect = address(0);
    }

    /**
    @dev See {IPatchworkProtocol-acceptScopeTransfer}
    */
    function acceptScopeTransfer(string calldata scopeName) public {
        Scope storage s = _mustHaveScope(scopeName);
        if (s.ownerElect == msg.sender) {
            address oldOwner = s.owner;
            s.owner = msg.sender;
            s.ownerElect = address(0);
            emit ScopeTransfer(scopeName, oldOwner, msg.sender);
        } else {
            revert NotAuthorized(msg.sender);
        }
    }

    /**
    @dev See {IPatchworkProtocol-getScopeOwnerElect}
    */
    function getScopeOwnerElect(string calldata scopeName) public view returns (address ownerElect) {
        return _scopes[scopeName].ownerElect;
    }

    /**
    @dev See {IPatchworkProtocol-getScopeOwner}
    */
    function getScopeOwner(string calldata scopeName) public view returns (address owner) {
        return _scopes[scopeName].owner;
    }

    /**
    @dev See {IPatchworkProtocol-addOperator}
    */
    function addOperator(string calldata scopeName, address op) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwner(s);
        s.operators[op] = true;
        emit ScopeAddOperator(scopeName, msg.sender, op);
    }

    /**
    @dev See {IPatchworkProtocol-removeOperator}
    */
    function removeOperator(string calldata scopeName, address op) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwner(s);
        s.operators[op] = false;
        emit ScopeRemoveOperator(scopeName, msg.sender, op);
    }

    /**
    @dev See {IPatchworkProtocol-setScopeRules}
    */
    function setScopeRules(string calldata scopeName, bool allowUserPatch, bool allowUserAssign, bool requireWhitelist) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwner(s);
        s.allowUserPatch = allowUserPatch;
        s.allowUserAssign = allowUserAssign;
        s.requireWhitelist = requireWhitelist;
        emit ScopeRuleChange(scopeName, msg.sender, allowUserPatch, allowUserAssign, requireWhitelist);
    }

    /**
    @dev See {IPatchworkProtocol-setMintConfiguration}
    */
    function setMintConfiguration(address addr, MintConfig memory config) public {
        if (!IERC165(addr).supportsInterface(type(IPatchworkMintable).interfaceId)) {
            revert UnsupportedContract();
        }
        string memory scopeName = _getScopeName(addr);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, addr);
        _mustBeOwnerOrOperator(scope);
        scope.mintConfigurations[addr] = config;
        emit MintConfigure(scopeName, msg.sender, addr, config);
    }

    /**
    @dev See {IPatchworkProtocol-getMintConfiguration}
    */
    function getMintConfiguration(address addr) public view returns (MintConfig memory config) {
        if (!IERC165(addr).supportsInterface(type(IPatchworkMintable).interfaceId)) {
            revert UnsupportedContract();
        }
        Scope storage scope = _mustHaveScope(_getScopeNameViewOnly(addr));
        return scope.mintConfigurations[addr];
    }

    /**
    @dev See {IPatchworkProtocol-setPatchFee}
    */
    function setPatchFee(address addr, uint256 baseFee) public {
        if (!IERC165(addr).supportsInterface(type(IPatchworkScoped).interfaceId)) {
            revert UnsupportedContract();
        }
        string memory scopeName = _getScopeName(addr);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, addr);
        _mustBeOwnerOrOperator(scope);
        scope.patchFees[addr] = baseFee;
        emit PatchFeeChange(scopeName, addr, baseFee);
    }

    /**
    @dev See {IPatchworkProtocol-getPatchFee}
    */
    function getPatchFee(address addr) public view returns (uint256 baseFee) {
        if (!IERC165(addr).supportsInterface(type(IPatchworkScoped).interfaceId)) {
            revert UnsupportedContract();
        }
        Scope storage scope = _mustHaveScope(_getScopeNameViewOnly(addr));
        return scope.patchFees[addr];
    }

    /**
    @dev See {IPatchworkProtocol-setAssignFee}
    */
    function setAssignFee(address fragmentAddress, uint256 baseFee) public {
        if (!IERC165(fragmentAddress).supportsInterface(type(IPatchworkScoped).interfaceId)) {
            revert UnsupportedContract();
        }
        string memory scopeName = _getScopeName(fragmentAddress);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, fragmentAddress);
        _mustBeOwnerOrOperator(scope);
        scope.assignFees[fragmentAddress] = baseFee;
        emit AssignFeeChange(scopeName, fragmentAddress, baseFee);
    }

    /**
    @dev See {IPatchworkProtocol-getAssignFee}
    */
    function getAssignFee(address fragmentAddress) public view returns (uint256 baseFee) {
        if (!IERC165(fragmentAddress).supportsInterface(type(IPatchworkScoped).interfaceId)) {
            revert UnsupportedContract();
        }
        Scope storage scope = _mustHaveScope(_getScopeNameViewOnly(fragmentAddress));
        return scope.assignFees[fragmentAddress];
    }

    /**
    @dev See {IPatchworkProtocol-addBanker}
    */
    function addBanker(string memory scopeName, address addr) public {
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeOwnerOrOperator(scope);
        scope.bankers[addr] = true;
        emit ScopeBankerAdd(scopeName, msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-removeBanker}
    */
    function removeBanker(string memory scopeName, address addr) public {
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeOwnerOrOperator(scope);
        delete scope.bankers[addr];
        emit ScopeBankerRemove(scopeName, msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-withdraw}
    */
    function withdraw(string memory scopeName, uint256 amount) public nonReentrant {
        Scope storage scope = _mustHaveScope(scopeName);
        if (msg.sender != scope.owner && !scope.bankers[msg.sender]) {
            revert NotAuthorized(msg.sender);
        }
        if (amount > scope.balance) {
            revert InsufficientFunds();
        }
        // modify state before calling to send
        scope.balance -= amount;
        // transfer funds
        (bool sent,) = msg.sender.call{value: amount}("");
        if (!sent) {
            revert FailedToSend();
        }
        emit ScopeWithdraw(scopeName, msg.sender, amount);
    }

    /**
    @dev See {IPatchworkProtocol-balanceOf}
    */
    function balanceOf(string memory scopeName) public view returns (uint256 balance) {
        Scope storage scope = _mustHaveScope(scopeName);
        return scope.balance;
    }

    /**
    @dev See {IPatchworkProtocol-mint}
    */
    function mint(address to, address mintable, bytes calldata data) external payable returns (uint256 tokenId) {
        (MintConfig memory config, string memory scopeName, Scope storage scope) = _setupMint(mintable);
        if (msg.value != config.flatFee) {
            revert IncorrectFeeAmount();
        }
        (uint256 scopeFee, uint256 protocolFee) = _handleMintFee(scopeName, scope);
        tokenId = IPatchworkMintable(mintable).mint(to, data);
        emit Mint(msg.sender, scopeName, to, mintable, data, scopeFee, protocolFee);
    }
    
    /**
    @dev See {IPatchworkProtocol-mintBatch}
    */
    function mintBatch(address to, address mintable, bytes calldata data, uint256 quantity) external payable returns (uint256[] memory tokenIds) {
        (MintConfig memory config, string memory scopeName, Scope storage scope) = _setupMint(mintable);
        uint256 totalFee = config.flatFee * quantity;
        if (msg.value != totalFee) {
            revert IncorrectFeeAmount();
        }
        (uint256 scopeFee, uint256 protocolFee) = _handleMintFee(scopeName, scope);
        tokenIds = IPatchworkMintable(mintable).mintBatch(to, data, quantity);
        emit MintBatch(msg.sender, scopeName, to, mintable, data, quantity, scopeFee, protocolFee);
    }

    /// Common to mints
    function _setupMint(address mintable) internal view returns (MintConfig memory config, string memory scopeName, Scope storage scope) {
        if (!IERC165(mintable).supportsInterface(type(IPatchworkMintable).interfaceId)) {
            revert UnsupportedContract();
        }
        scopeName = _getScopeNameViewOnly(mintable);
        scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, mintable);
        config = scope.mintConfigurations[mintable];
        if (!config.active) {
            revert MintNotActive();
        }
    }

    /// Common to mints
    function _handleMintFee(string memory scopeName, Scope storage scope) internal returns (uint256 scopeFee, uint256 protocolFee) {
        // Account for 100% of the message value
        if (msg.value != 0) {
            uint256 mintBp;
            FeeConfigOverride storage feeOverride = _scopeFeeOverrides[scopeName];
            if (feeOverride.active) {
                mintBp = feeOverride.mintBp;
            } else {
                mintBp = _protocolFeeConfig.mintBp;
            }
            protocolFee = msg.value * mintBp / _FEE_BASIS_DENOM;
            scopeFee = msg.value - protocolFee;
            _protocolBalance += protocolFee;
            scope.balance += scopeFee;
        }
    }

    /**
    @dev See {IPatchworkProtocol-proposeProtocolFeeConfig}
    */
    function proposeProtocolFeeConfig(FeeConfig memory config) public onlyProtoOwnerBanker {
        if (config.assignBp > _PROTOCOL_FEE_CEILING || config.mintBp > _PROTOCOL_FEE_CEILING || config.patchBp > _PROTOCOL_FEE_CEILING) {
            revert InvalidFeeValue();
        }
        _proposedFeeConfigs[""] = ProposedFeeConfig(config, block.timestamp, true);
        emit ProtocolFeeConfigPropose(config);
    }

    /**
    @dev See {IPatchworkProtocol-commitProtocolFeeConfig}
    */
    function commitProtocolFeeConfig() public onlyProtoOwnerBanker {
        (FeeConfig memory config, /* bool active */) = _preCommitFeeChange("");
        _protocolFeeConfig = config;
        emit ProtocolFeeConfigCommit(_protocolFeeConfig);
    }

    /**
    @dev See {IPatchworkProtocol-getProtocolFeeConfig}
    */
    function getProtocolFeeConfig() public view returns (FeeConfig memory config) {
        return _protocolFeeConfig;
    }

    /**
    @dev See {IPatchworkProtocol-proposeScopeFeeOverride}
    */
    function proposeScopeFeeOverride(string memory scopeName, FeeConfigOverride memory config) public onlyProtoOwnerBanker {
        if (config.assignBp > _PROTOCOL_FEE_CEILING || config.mintBp > _PROTOCOL_FEE_CEILING || config.patchBp > _PROTOCOL_FEE_CEILING) {
            revert InvalidFeeValue();
        }
        _proposedFeeConfigs[scopeName] = ProposedFeeConfig(
            FeeConfig(config.mintBp, config.patchBp, config.assignBp), block.timestamp, config.active);
        emit ScopeFeeOverridePropose(scopeName, config);
    }

    /**
    @dev See {IPatchworkProtocol-commitScopeFeeOverride}
    */
    function commitScopeFeeOverride(string memory scopeName) public onlyProtoOwnerBanker {
        (FeeConfig memory config, bool active) = _preCommitFeeChange(scopeName);
        FeeConfigOverride memory feeOverride = FeeConfigOverride(config.mintBp, config.patchBp, config.assignBp, active);
        if (!active) {
            delete _scopeFeeOverrides[scopeName];
        } else {
            _scopeFeeOverrides[scopeName] = feeOverride;
        }
        emit ScopeFeeOverrideCommit(scopeName, feeOverride);
    }

    /**
    @dev commits a fee change if a proposal exists and timelock is satisfied
    @param scopeName "" for protocol or the scope name
    @return config The proposed config
    @return active The proposed active state (only applies to fee overrides)
    */
    function _preCommitFeeChange(string memory scopeName) private returns (FeeConfig memory config, bool active) {
        ProposedFeeConfig storage proposal = _proposedFeeConfigs[scopeName];
        if (proposal.timestamp == 0) {
            revert NoProposedFeeSet();
        }
        if (block.timestamp < proposal.timestamp + FEE_CHANGE_TIMELOCK) {
            revert TimelockNotElapsed();
        }
        config = proposal.config;
        active = proposal.active;
        delete _proposedFeeConfigs[scopeName];
    }

    /**
    @dev See {IPatchworkProtocol-getScopeFeeOverride}
    */
    function getScopeFeeOverride(string memory scopeName) public view returns (FeeConfigOverride memory config) {
        return _scopeFeeOverrides[scopeName];
    }

    /**
    @dev See {IPatchworkProtocol-addProtocolBanker}
    */
    function addProtocolBanker(address addr) external onlyOwner {
        _protocolBankers[addr] = true;
        emit ProtocolBankerAdd(msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-removeProtocolBanker}
    */
    function removeProtocolBanker(address addr) external onlyOwner {
        delete _protocolBankers[addr];
        emit ProtocolBankerRemove(msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-withdrawFromProtocol}
    */
    function withdrawFromProtocol(uint256 amount) external nonReentrant onlyProtoOwnerBanker {
        if (amount > _protocolBalance) {
            revert InsufficientFunds();
        }
        _protocolBalance -= amount;
         (bool sent,) = msg.sender.call{value: amount}("");
        if (!sent) {
            revert FailedToSend();
        }
        emit ProtocolWithdraw(msg.sender, amount);
    }

    function balanceOfProtocol() public view returns (uint256 balance) {
        return _protocolBalance;
    }

    /**
    @dev See {IPatchworkProtocol-addWhitelist}
    */
    function addWhitelist(string calldata scopeName, address addr) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwnerOrOperator(s);
        s.whitelist[addr] = true;
        emit ScopeWhitelistAdd(scopeName, msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-removeWhitelist}
    */
    function removeWhitelist(string calldata scopeName, address addr) public {
        Scope storage s = _mustHaveScope(scopeName);
        _mustBeOwnerOrOperator(s);
        s.whitelist[addr] = false;
        emit ScopeWhitelistRemove(scopeName, msg.sender, addr);
    }

    /**
    @dev See {IPatchworkProtocol-patch}
    */
    function patch(address owner, address originalAddress, uint originalTokenId, address patchAddress) external payable returns (uint256 tokenId) {
        if (!IERC165(patchAddress).supportsInterface(type(IPatchworkPatch).interfaceId)) {
            revert UnsupportedContract();
        }
        IPatchworkPatch patch_ = IPatchworkPatch(patchAddress);
        string memory scopeName = _getScopeName(patchAddress);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, patchAddress);
        if (scope.owner == msg.sender || scope.operators[msg.sender]) {
            // continue
        } else if (scope.allowUserPatch) {
            // continue
        } else {
            revert NotAuthorized(msg.sender);
        }
        (uint256 scopeFee, uint256 protocolFee) = _handlePatchFee(scopeName, scope, patchAddress);
        // limit this to one unique patch (originalAddress+TokenID+patchAddress)
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, originalTokenId, patchAddress));
        if (_uniquePatches[_hash]) {
            revert AlreadyPatched(originalAddress, originalTokenId, patchAddress);
        }
        _uniquePatches[_hash] = true;
        tokenId = patch_.mintPatch(owner, IPatchworkPatch.PatchTarget(originalAddress, originalTokenId));
        emit Patch(owner, originalAddress, originalTokenId, patchAddress, tokenId, scopeFee, protocolFee);
        return tokenId;
    }

    /**
    @dev See {IPatchworkProtocol-patchBurned}
    */
    function patchBurned(address originalAddress, uint originalTokenId, address patchAddress) external onlyFrom(patchAddress) {
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, originalTokenId, patchAddress));
        delete _uniquePatches[_hash];
    }

    /**
    @dev See {IPatchworkProtocol-patch1155}
    */
    function patch1155(address to, address originalAddress, uint originalTokenId, address originalAccount, address patchAddress) external payable returns (uint256 tokenId) {
        if (!IERC165(patchAddress).supportsInterface(type(IPatchwork1155Patch).interfaceId)) {
            revert UnsupportedContract();
        }
        IPatchwork1155Patch patch_ = IPatchwork1155Patch(patchAddress);
        string memory scopeName = _getScopeName(patchAddress);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, patchAddress);
        if (scope.owner == msg.sender || scope.operators[msg.sender]) {
            // continue
        } else if (scope.allowUserPatch) {
            // continue
        } else {
            revert NotAuthorized(msg.sender);
        }
        (uint256 scopeFee, uint256 protocolFee) = _handlePatchFee(scopeName, scope, patchAddress);
        // limit this to one unique patch (originalAddress+TokenID+patchAddress)
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, originalTokenId, originalAccount, patchAddress));
        if (_uniquePatches[_hash]) {
            revert ERC1155AlreadyPatched(originalAddress, originalTokenId, originalAccount, patchAddress);
        }
        _uniquePatches[_hash] = true;
        tokenId = patch_.mintPatch(to, IPatchwork1155Patch.PatchTarget(originalAddress, originalTokenId, originalAccount));
        emit ERC1155Patch(to, originalAddress, originalTokenId, originalAccount, patchAddress, tokenId, scopeFee, protocolFee);
        return tokenId;
    }

    /**
    @dev See {IPatchworkProtocol-patchBurned1155}
    */
    function patchBurned1155(address originalAddress, uint originalTokenId, address originalAccount, address patchAddress) external onlyFrom(patchAddress) {
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, originalTokenId, originalAccount, patchAddress));
        delete _uniquePatches[_hash];
    }
    
    /**
    @dev See {IPatchworkProtocol-patchAccount}
    */
    function patchAccount(address owner, address originalAddress, address patchAddress) external payable returns (uint256 tokenId) {
        if (!IERC165(patchAddress).supportsInterface(type(IPatchworkAccountPatch).interfaceId)) {
            revert UnsupportedContract();
        }
        IPatchworkAccountPatch patch_ = IPatchworkAccountPatch(patchAddress);
        string memory scopeName = _getScopeName(patchAddress);
        Scope storage scope = _mustHaveScope(scopeName);
        _mustBeWhitelisted(scopeName, scope, patchAddress);
        if (scope.owner == msg.sender || scope.operators[msg.sender]) {
            // continue
        } else if (scope.allowUserPatch) { // This allows any user to patch any address
            // continue
        } else {
            revert NotAuthorized(msg.sender);
        }
        (uint256 scopeFee, uint256 protocolFee) = _handlePatchFee(scopeName, scope, patchAddress);
        // limit this to one unique patch (originalAddress+patchAddress)
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, patchAddress));
        if (_uniquePatches[_hash]) {
            revert AccountAlreadyPatched(originalAddress, patchAddress);
        }
        _uniquePatches[_hash] = true;
        tokenId = patch_.mintPatch(owner, originalAddress);
        emit AccountPatch(owner, originalAddress, patchAddress, tokenId, scopeFee, protocolFee);
        return tokenId;
    }

    /**
    @dev See {IPatchworkProtocol-patchBurnedAccount}
    */
    function patchBurnedAccount(address originalAddress, address patchAddress) external onlyFrom(patchAddress) {
        bytes32 _hash = keccak256(abi.encodePacked(originalAddress, patchAddress));
        delete _uniquePatches[_hash];
    }

    /// common to patches
    function _handlePatchFee(string memory scopeName, Scope storage scope, address patchAddress) private returns (uint256 scopeFee, uint256 protocolFee) {
        uint256 patchFee = scope.patchFees[patchAddress];
        if (msg.value != patchFee) {
            revert IncorrectFeeAmount();
        }
        if (msg.value > 0) {
            uint256 patchBp;
            FeeConfigOverride storage feeOverride = _scopeFeeOverrides[scopeName];
            if (feeOverride.active) {
                patchBp = feeOverride.patchBp;
            } else {
                patchBp = _protocolFeeConfig.patchBp;
            }
            protocolFee = msg.value * patchBp / _FEE_BASIS_DENOM;
            scopeFee = msg.value - protocolFee;
            _protocolBalance += protocolFee;
            scope.balance += scopeFee;
        }
    }

    function _delegatecall(address delegate, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = delegate.delegatecall(data);
        if (!success) {
            if (returndata.length == 0) revert();
            assembly {
                revert(add(32, returndata), mload(returndata))
            }
        }
        return returndata;
    }

    /**
    @dev See {IPatchworkProtocol-assign}
    */
    function assign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) public payable {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("assign(address,uint256,address,uint256)", fragment, fragmentTokenId, target, targetTokenId));
    }

    /**
    @dev See {IPatchworkProtocol-assign}
    */
    function assign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) public payable {
         _delegatecall(_assignerDelegate, abi.encodeWithSignature("assign(address,uint256,address,uint256,uint256)", fragment, fragmentTokenId, target, targetTokenId, targetMetadataId));
   }

    /**
    @dev See {IPatchworkProtocol-assignBatch}
    */
    function assignBatch(address[] calldata fragments, uint256[] calldata tokenIds, address target, uint256 targetTokenId) public payable {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("assignBatch(address[],uint256[],address,uint256)", fragments, tokenIds, target, targetTokenId));
    }

    /**
    @dev See {IPatchworkProtocol-assignBatch}
    */
    function assignBatch(address[] calldata fragments, uint256[] calldata tokenIds, address target, uint256 targetTokenId, uint256 targetMetadataId) public payable {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("assignBatch(address[],uint256[],address,uint256,uint256)", fragments, tokenIds, target, targetTokenId, targetMetadataId));
    }
    
    /**
    @dev See {IPatchworkProtocol-unassign}
    */
    function unassign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassign(address,uint256,address,uint256)", fragment, fragmentTokenId, target, targetTokenId));
    }

    /**
    @dev See {IPatchworkProtocol-unassign}
    */
    function unassign(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassign(address,uint256,address,uint256,uint256)", fragment, fragmentTokenId, target, targetTokenId, targetMetadataId));
    }

    /**
    @dev See {IPatchworkProtocol-unassignMulti}
    */
    function unassignMulti(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassignMulti(address,uint256,address,uint256)", fragment, fragmentTokenId, target, targetTokenId));
    }

    /**
    @dev See {IPatchworkProtocol-unassignMulti}
    */
    function unassignMulti(address fragment, uint256 fragmentTokenId, address target, uint256 targetTokenId, uint256 targetMetadataId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassignMulti(address,uint256,address,uint256,uint256)", fragment, fragmentTokenId, target, targetTokenId, targetMetadataId));
    }

    /**
    @dev See {IPatchworkProtocol-unassignSingle}
    */
    function unassignSingle(address fragment, uint256 fragmentTokenId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassignSingle(address,uint256)", fragment, fragmentTokenId));
    }

    /**
    @dev See {IPatchworkProtocol-unassignSingle}
    */
    function unassignSingle(address fragment, uint256 fragmentTokenId, uint256 targetMetadataId) public {
        _delegatecall(_assignerDelegate, abi.encodeWithSignature("unassignSingle(address,uint256,uint256)", fragment, fragmentTokenId, targetMetadataId));
    }

    /**
    @dev See {IPatchworkProtocol-applyTransfer}
    */
    function applyTransfer(address from, address to, uint256 tokenId) public {
        address nft = msg.sender;
        if (IERC165(nft).supportsInterface(type(IPatchworkSingleAssignable).interfaceId)) {
            IPatchworkSingleAssignable assignable = IPatchworkSingleAssignable(nft);
            (address addr,) = assignable.getAssignedTo(tokenId);
            if (addr != address(0)) {
                revert TransferBlockedByAssignment(nft, tokenId);
            }
        }
        if (IERC165(nft).supportsInterface(type(IPatchworkPatch).interfaceId)) {
            revert TransferNotAllowed(nft, tokenId);
        }
        if (IERC165(nft).supportsInterface(type(IPatchwork721).interfaceId)) {
            if (IPatchwork721(nft).locked(tokenId)) {
                revert Locked(nft, tokenId);
            }
        }
        if (IERC165(nft).supportsInterface(type(IPatchworkLiteRef).interfaceId)) {
            (address[] memory addresses, uint256[] memory tokenIds) = IPatchworkLiteRef(nft).loadAllStaticReferences(tokenId);
            for (uint i = 0; i < addresses.length; i++) {
                if (addresses[i] != address(0)) {
                    _applyAssignedTransfer(addresses[i], from, to, tokenIds[i], nft, tokenId);
                }
            }
        }
    }

    function _applyAssignedTransfer(address nft, address from, address to, uint256 tokenId, address assignedTo_, uint256 assignedToTokenId_) private {
        if (!IERC165(nft).supportsInterface(type(IPatchworkSingleAssignable).interfaceId)) {
            revert NotPatchworkAssignable(nft);
        }
        (address assignedTo, uint256 assignedToTokenId) = IPatchworkSingleAssignable(nft).getAssignedTo(tokenId);
        // 2-way Check the assignment to prevent spoofing
        if (assignedTo_ != assignedTo || assignedToTokenId_ != assignedToTokenId) {
            revert DataIntegrityError(assignedTo_, assignedToTokenId_, assignedTo, assignedToTokenId);
        }
        IPatchworkSingleAssignable(nft).onAssignedTransfer(from, to, tokenId);
        if (IERC165(nft).supportsInterface(type(IPatchworkLiteRef).interfaceId)) {
            address nft_ = nft; // local variable prevents optimizer stack issue in v0.8.18
            (address[] memory addresses, uint256[] memory tokenIds) = IPatchworkLiteRef(nft).loadAllStaticReferences(tokenId);
            for (uint i = 0; i < addresses.length; i++) {
                if (addresses[i] != address(0)) {
                    _applyAssignedTransfer(addresses[i], from, to, tokenIds[i], nft_, tokenId);
                }
            }
        }
    }

    /**
    @dev See {IPatchworkProtocol-updateOwnershipTree}
    */ 
    function updateOwnershipTree(address addr, uint256 tokenId) public {
        if (IERC165(addr).supportsInterface(type(IPatchworkLiteRef).interfaceId)) {
            (address[] memory addresses, uint256[] memory tokenIds) = IPatchworkLiteRef(addr).loadAllStaticReferences(tokenId);
            for (uint i = 0; i < addresses.length; i++) {
                if (addresses[i] != address(0)) {
                    updateOwnershipTree(addresses[i], tokenIds[i]);
                }
            }
        }
        if (IERC165(addr).supportsInterface(type(IPatchworkSingleAssignable).interfaceId)) {
            IPatchworkSingleAssignable(addr).updateOwnership(tokenId);
        } else if (IERC165(addr).supportsInterface(type(IPatchworkPatch).interfaceId)) {
            IPatchworkPatch(addr).updateOwnership(tokenId);
        }
    }

    /**
    @dev See {IPatchworkProtocol-proposeAssignerDelegate}
    */
    function proposeAssignerDelegate(address addr) public onlyOwner {
        if (addr == address(0)) {
            // effectively a cancel
            _proposedAssignerDelegate = ProposedAssignerDelegate(0, address(0));
        } else {
            _proposedAssignerDelegate = ProposedAssignerDelegate(block.timestamp, addr);
        }
        emit AssignerDelegatePropose(addr);
    }

    /**
    @dev See {IPatchworkProtocol-commitAssignerDelegate}
    */
    function commitAssignerDelegate() public onlyOwner {
        if (_proposedAssignerDelegate.timestamp == 0) {
            revert NoDelegateProposed();
        }
        if (block.timestamp < _proposedAssignerDelegate.timestamp + CONTRACT_UPGRADE_TIMELOCK) {
            revert TimelockNotElapsed();
        }
        _assignerDelegate = _proposedAssignerDelegate.addr;
        _proposedAssignerDelegate = ProposedAssignerDelegate(0, address(0));
        emit AssignerDelegateCommit(_assignerDelegate);
    }

    /**
    @notice Requires that msg.sender is owner of scope
    @dev will revert with NotAuthorized if msg.sender is not owner
    @param scope the scope
    */
    function _mustBeOwner(Scope storage scope) private view {
        if (msg.sender != scope.owner) {
            revert NotAuthorized(msg.sender);
        }
    }

    /**
    @notice Requires that msg.sender is owner or operator of scope
    @dev will revert with NotAuthorized if msg.sender is not owner or operator
    @param scope the scope
    */
    function _mustBeOwnerOrOperator(Scope storage scope) private view {
        if (msg.sender != scope.owner && !scope.operators[msg.sender]) {
            revert NotAuthorized(msg.sender);
        }
    }

    /**
    @notice Memoized view-only wrapper for IPatchworkScoped.getScopeName()
    @dev required to get optimized result from view-only functions, does not memoize result if not already memoized
    @param addr Address to check
    @return scopeName return value of IPatchworkScoped(addr).getScopeName()
    */
    function _getScopeNameViewOnly(address addr) private view returns (string memory scopeName) {
        scopeName = _scopeNameCache[addr];
        if (bytes(scopeName).length == 0) {
            scopeName = IPatchworkScoped(addr).getScopeName();
        }
    }

    /// Only protocol owner or protocol banker
    modifier onlyProtoOwnerBanker() {
        if (msg.sender != owner() && _protocolBankers[msg.sender] == false) {
            revert NotAuthorized(msg.sender);
        }
        _;
    }

    /// Only msg.sender from addr
    modifier onlyFrom(address addr) {
        if (msg.sender != addr) {
            revert NotAuthorized(msg.sender);
        }
        _;
    }
}
合同源代码
文件 18 的 19:PatchworkProtocolCommon.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**

    ____        __       __                       __  
   / __ \____ _/ /______/ /_ _      ______  _____/ /__
  / /_/ / __ `/ __/ ___/ __ \ | /| / / __ \/ ___/ //_/
 / ____/ /_/ / /_/ /__/ / / / |/ |/ / /_/ / /  / ,<   
/_/ ___\__,_/\__/\___/_/ /_/|__/|__/\____/_/  /_/|_|  
   / __ \_________  / /_____  _________  / /          
  / /_/ / ___/ __ \/ __/ __ \/ ___/ __ \/ /           
 / ____/ /  / /_/ / /_/ /_/ / /__/ /_/ / /            
/_/   /_/   \____/\__/\____/\___/\____/_/          

Storage layout and common functions

*/

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces/IPatchworkProtocol.sol";
import "./interfaces/IPatchworkScoped.sol";

/** 
@title Patchwork Protocol Storage layout and common functions
@author Runic Labs, Inc
*/
contract PatchworkProtocolCommon is Ownable, ReentrancyGuard{

    constructor(address owner_) Ownable(owner_) {}

    /// Scopes
    mapping(string => IPatchworkProtocol.Scope) internal _scopes;

    /**
    @notice unique references
    @dev A hash of target + targetTokenId + literef provides uniqueness
    */
    mapping(bytes32 => bool) internal _liteRefs;

    /**
    @notice unique patches
    @dev Hash of the patch mapped to a boolean indicating its uniqueness
    */
    mapping(bytes32 => bool) internal _uniquePatches;

    /// Balance of the protocol
    uint256 internal _protocolBalance;

    /**
    @notice protocol bankers
    @dev Map of addresses authorized to set fees and withdraw funds for the protocol
    @dev Does not allow for scope balance withdrawl
    */
    mapping(address => bool) internal _protocolBankers;

    /// Current protocol fee configuration
    IPatchworkProtocol.FeeConfig internal _protocolFeeConfig;

    /// Proposed protocol fee configuration
    mapping(string => IPatchworkProtocol.ProposedFeeConfig) internal _proposedFeeConfigs;

    /// scope-based fee overrides
    mapping(string => IPatchworkProtocol.FeeConfigOverride) internal _scopeFeeOverrides; 

    /// Scope name cache
    mapping(address => string) internal _scopeNameCache;

    /// Proposed assigner delegate
    IPatchworkProtocol.ProposedAssignerDelegate internal _proposedAssignerDelegate;

    /// Assigner module
    address internal _assignerDelegate;

    /**
    @notice Memoizing wrapper for IPatchworkScoped.getScopeName()
    @param addr Address to check
    @return scopeName return value of IPatchworkScoped(addr).getScopeName()
    */
    function _getScopeName(address addr) internal returns (string memory scopeName) {
        scopeName = _scopeNameCache[addr];
        if (bytes(scopeName).length == 0) {
            scopeName = IPatchworkScoped(addr).getScopeName();
            _scopeNameCache[addr] = scopeName;
        }
    }

    /**
    @notice Requires that scopeName is present
    @dev will revert with ScopeDoesNotExist if not present
    @return scope the scope
    */
    function _mustHaveScope(string memory scopeName) internal view returns (IPatchworkProtocol.Scope storage scope) {
        scope = _scopes[scopeName];
        if (scope.owner == address(0)) {
            revert IPatchworkProtocol.ScopeDoesNotExist(scopeName);
        }
    }

    /**
    @notice Requires that addr is whitelisted if whitelisting is enabled
    @dev will revert with NotWhitelisted if whitelisting is enabled and address is not whitelisted
    @param scopeName the name of the scope
    @param scope the scope
    @param addr the address to check
    */
    function _mustBeWhitelisted(string memory scopeName, IPatchworkProtocol.Scope storage scope, address addr) internal view {
        if (scope.requireWhitelist && !scope.whitelist[addr]) {
            revert IPatchworkProtocol.NotWhitelisted(scopeName, addr);
        }
    }
}
合同源代码
文件 19 的 19:ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
设置
{
  "compilationTarget": {
    "src/PatchworkProtocol.sol": "PatchworkProtocol"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":@openzeppelin/=lib/openzeppelin-contracts/",
    ":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    ":ds-test/=lib/forge-std/lib/ds-test/src/",
    ":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    ":forge-std/=lib/forge-std/src/",
    ":openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ]
}
ABI
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"assignerDelegate_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"AccountAlreadyPatched","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"AlreadyPatched","type":"error"},{"inputs":[],"name":"BadInputLengths","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"CannotLockSoulboundPatch","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"addr2","type":"address"},{"internalType":"uint256","name":"tokenId2","type":"uint256"}],"name":"DataIntegrityError","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"ERC1155AlreadyPatched","type":"error"},{"inputs":[],"name":"FailedToSend","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"FragmentAlreadyAssigned","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"FragmentAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"FragmentNotAssigned","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"FragmentNotAssignedToTarget","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"FragmentRedacted","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"FragmentUnregistered","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Frozen","type":"error"},{"inputs":[],"name":"IncorrectFeeAmount","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"IncorrectNonce","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidFeeValue","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Locked","type":"error"},{"inputs":[],"name":"MintNotActive","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"MintNotAllowed","type":"error"},{"inputs":[],"name":"NoDelegateProposed","type":"error"},{"inputs":[],"name":"NoProposedFeeSet","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"NotAuthorized","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotFrozen","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"NotPatchworkAssignable","type":"error"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"addr","type":"address"}],"name":"NotWhitelisted","type":"error"},{"inputs":[],"name":"OutOfIDs","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RefNotFound","type":"error"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"ScopeDoesNotExist","type":"error"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"ScopeExists","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"ScopeTransferNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"SelfAssignmentNotAllowed","type":"error"},{"inputs":[],"name":"TimelockNotElapsed","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TransferBlockedByAssignment","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TransferNotAllowed","type":"error"},{"inputs":[],"name":"UnsupportedContract","type":"error"},{"inputs":[],"name":"UnsupportedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"UnsupportedTokenId","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"originalAddress","type":"address"},{"indexed":true,"internalType":"address","name":"patchAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"patchTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"AccountPatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"fragmentAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"targetTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"Assign","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"AssignFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"AssignerDelegateCommit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"AssignerDelegatePropose","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"originalAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"originalAccount","type":"address"},{"indexed":true,"internalType":"address","name":"patchAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"patchTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"ERC1155Patch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"mintable","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"mintable","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"MintBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"mintable","type":"address"},{"components":[{"internalType":"uint256","name":"flatFee","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"indexed":false,"internalType":"struct IPatchworkProtocol.MintConfig","name":"config","type":"tuple"}],"name":"MintConfigure","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"originalAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"patchAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"patchTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scopeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"Patch","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"PatchFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"banker","type":"address"}],"name":"ProtocolBankerAdd","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"banker","type":"address"}],"name":"ProtocolBankerRemove","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"}],"indexed":false,"internalType":"struct IPatchworkProtocol.FeeConfig","name":"config","type":"tuple"}],"name":"ProtocolFeeConfigCommit","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"}],"indexed":false,"internalType":"struct IPatchworkProtocol.FeeConfig","name":"config","type":"tuple"}],"name":"ProtocolFeeConfigPropose","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ProtocolWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"ScopeAddOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"banker","type":"address"}],"name":"ScopeBankerAdd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"banker","type":"address"}],"name":"ScopeBankerRemove","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"ScopeClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"indexed":false,"internalType":"struct IPatchworkProtocol.FeeConfigOverride","name":"config","type":"tuple"}],"name":"ScopeFeeOverrideCommit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"indexed":false,"internalType":"struct IPatchworkProtocol.FeeConfigOverride","name":"config","type":"tuple"}],"name":"ScopeFeeOverridePropose","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"ScopeRemoveOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":false,"internalType":"bool","name":"allowUserPatch","type":"bool"},{"indexed":false,"internalType":"bool","name":"allowUserAssign","type":"bool"},{"indexed":false,"internalType":"bool","name":"requireWhitelist","type":"bool"}],"name":"ScopeRuleChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ScopeTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ScopeTransferCancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ScopeTransferElect","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"ScopeWhitelistAdd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"ScopeWhitelistRemove","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"scopeName","type":"string"},{"indexed":true,"internalType":"address","name":"actor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ScopeWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"fragmentAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"Unassign","type":"event"},{"inputs":[],"name":"CONTRACT_UPGRADE_TIMELOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_CHANGE_TIMELOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"acceptScopeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"addr","type":"address"}],"name":"addBanker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"op","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"addProtocolBanker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"addr","type":"address"}],"name":"addWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"applyTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"assign","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"},{"internalType":"uint256","name":"targetMetadataId","type":"uint256"}],"name":"assign","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"fragments","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"assignBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"fragments","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"},{"internalType":"uint256","name":"targetMetadataId","type":"uint256"}],"name":"assignBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceOfProtocol","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"cancelScopeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"claimScope","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"commitAssignerDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"commitProtocolFeeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"commitScopeFeeOverride","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragmentAddress","type":"address"}],"name":"getAssignFee","outputs":[{"internalType":"uint256","name":"baseFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getMintConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"flatFee","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"internalType":"struct IPatchworkProtocol.MintConfig","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getPatchFee","outputs":[{"internalType":"uint256","name":"baseFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeeConfig","outputs":[{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"}],"internalType":"struct IPatchworkProtocol.FeeConfig","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"getScopeFeeOverride","outputs":[{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"internalType":"struct IPatchworkProtocol.FeeConfigOverride","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"getScopeOwner","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"}],"name":"getScopeOwnerElect","outputs":[{"internalType":"address","name":"ownerElect","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"mintable","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"mintable","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintBatch","outputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patch","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"internalType":"address","name":"originalAccount","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patch1155","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patchAccount","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patchBurned","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"uint256","name":"originalTokenId","type":"uint256"},{"internalType":"address","name":"originalAccount","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patchBurned1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"originalAddress","type":"address"},{"internalType":"address","name":"patchAddress","type":"address"}],"name":"patchBurnedAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"proposeAssignerDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"}],"internalType":"struct IPatchworkProtocol.FeeConfig","name":"config","type":"tuple"}],"name":"proposeProtocolFeeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"components":[{"internalType":"uint256","name":"mintBp","type":"uint256"},{"internalType":"uint256","name":"patchBp","type":"uint256"},{"internalType":"uint256","name":"assignBp","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"internalType":"struct IPatchworkProtocol.FeeConfigOverride","name":"config","type":"tuple"}],"name":"proposeScopeFeeOverride","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"addr","type":"address"}],"name":"removeBanker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"op","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"removeProtocolBanker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"addr","type":"address"}],"name":"removeWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragmentAddress","type":"address"},{"internalType":"uint256","name":"baseFee","type":"uint256"}],"name":"setAssignFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"components":[{"internalType":"uint256","name":"flatFee","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"internalType":"struct IPatchworkProtocol.MintConfig","name":"config","type":"tuple"}],"name":"setMintConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"baseFee","type":"uint256"}],"name":"setPatchFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"bool","name":"allowUserPatch","type":"bool"},{"internalType":"bool","name":"allowUserAssign","type":"bool"},{"internalType":"bool","name":"requireWhitelist","type":"bool"}],"name":"setScopeRules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferScopeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"},{"internalType":"uint256","name":"targetMetadataId","type":"uint256"}],"name":"unassign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"unassign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"},{"internalType":"uint256","name":"targetMetadataId","type":"uint256"}],"name":"unassignMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"targetTokenId","type":"uint256"}],"name":"unassignMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"},{"internalType":"uint256","name":"targetMetadataId","type":"uint256"}],"name":"unassignSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fragment","type":"address"},{"internalType":"uint256","name":"fragmentTokenId","type":"uint256"}],"name":"unassignSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"updateOwnershipTree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"scopeName","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFromProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"}]