-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfactory-standard-json.json
More file actions
1 lines (1 loc) · 118 KB
/
factory-standard-json.json
File metadata and controls
1 lines (1 loc) · 118 KB
1
{"language":"Solidity","sources":{"contracts/KonduxBeaconFactory.sol":{"content":"// SPDX‑License‑Identifier: GPL‑3.0\npragma solidity ^0.8.30;\n\nimport \"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\";\nimport \"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\";\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport \"@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol\";\nimport \"./KonduxRoyaltySplitter.sol\";\nimport \"./interfaces/IKonduxRoyaltySplitter.sol\";\n\n/**\n * @title KonduxBeaconFactory\n * @notice Deploy once, then mint BeaconProxy clones whose implementation address\n * is stored in a single UpgradeableBeacon.\n *\n * • `publicDeployment` flag: \n * – `true` (default) ⇒ anyone can call `deployClone()` \n * – `false` ⇒ only `CLONE_DEPLOYER_ROLE` addresses can. \n *\n * • `upgradeImplementation()` (admin‑only) changes the beacon’s\n * implementation, upgrading every past and future clone at once.\n */\ncontract KonduxBeaconFactory is AccessControl {\n /*-------------------------------------------------------------*/\n /* Roles & storage */\n /*-------------------------------------------------------------*/\n bytes32 public constant CLONE_DEPLOYER_ROLE = keccak256(\"CLONE_DEPLOYER\");\n bytes32 public constant FEE_ADMIN_ROLE = keccak256(\"FEE_ADMIN_ROLE\");\n\n UpgradeableBeacon public immutable beacon;\n bool public publicDeployment = true; // open by default\n\n /// @notice Seaport 1.6 contract address (mainnet)\n address public constant SEAPORT = 0x0000000000000068F116a894984e2DB1123eB395;\n\n /// @notice OpenSea Conduit address (mainnet)\n address public constant OPENSEA_CONDUIT = 0x1E0049783F008A0085193E00003D00cd54003c71;\n\n /// @notice Maps collection addresses to their deployed splitters\n mapping(address => address) public collectionToSplitter;\n\n /// @notice List of all deployed splitters for enumeration\n address[] public deployedSplitters;\n\n /*-------------------------------------------------------------*/\n /* Constructor */\n /*-------------------------------------------------------------*/\n /**\n * @dev Deploys a new UpgradeableBeacon with the given implementation address.\n * The factory itself is the owner of the beacon, so it can upgrade it later.\n * @notice The `impl` address must be a contract.\n * This contract does not check that, so it is the caller's responsibility.\n * @param impl The address of the implementation contract.\n */\n constructor(address impl) {\n _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\n _grantRole(FEE_ADMIN_ROLE, msg.sender);\n\n // Two‑arg constructor since OZ v5.x\n beacon = new UpgradeableBeacon(impl, address(this));\n // owner of the beacon == this factory (no transferOwnership needed)\n }\n\n /*-------------------------------------------------------------*/\n /* Admin functions */\n /*-------------------------------------------------------------*/\n\n /**\n * @dev Toggle open/gated clone deployment.\n * * If `open` is `true`, anyone can call `deployClone()`.\n * * If `open` is `false`, only addresses with the `CLONE_DEPLOYER_ROLE` can call `deployClone()`.\n * @param open Whether to allow public deployment or not\n */\n function setPublicDeployment(bool open)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n publicDeployment = open;\n emit PublicDeploymentChanged(open);\n }\n\n /**\n * @dev Upgrades the beacon's implementation to a new address.\n * This will affect all past and future clones created with this beacon.\n * @param newImpl The address of the new implementation contract.\n */\n function upgradeImplementation(address newImpl)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n beacon.upgradeTo(newImpl); // owner is the factory → OK\n emit GlobalUpgrade(newImpl);\n }\n\n /*-------------------------------------------------------------*/\n /* Clone deployment */\n /*-------------------------------------------------------------*/\n /**\n * @dev Deploys a new BeaconProxy clone with the given initialization calldata.\n * The clone will use the current implementation address stored in the beacon.\n * If `publicDeployment` is `false`, only addresses with the `CLONE_DEPLOYER_ROLE`\n * can call this function.\n * @notice The `initCalldata` is used to initialize the clone after deployment.\n * It should be the calldata for a initialize function in the implementation contract.\n * Automatically configures marketplace security (whitelists Seaport & conduit,\n * restricts other secondary markets).\n * @param initCalldata The initialization calldata to pass to the clone.\n */\n function deployClone(bytes calldata initCalldata)\n external\n returns (address proxy)\n {\n if (!publicDeployment) {\n _checkRole(CLONE_DEPLOYER_ROLE, msg.sender);\n }\n\n proxy = address(new BeaconProxy(address(beacon), initCalldata));\n\n // Configure marketplace security (whitelist Seaport & conduit, restrict other markets)\n _configureMarketplaceSecurity(proxy);\n\n emit CloneDeployed(proxy, msg.sender);\n }\n\n /**\n * @notice Deploys a BeaconProxy clone with an optional KonduxRoyaltySplitter.\n * @param initCalldata Initialization data for KonduxImplementation.\n * @param deploySplitter If true, also deploy a splitter for this collection.\n * @param partnerWallet Partner address for royalty splits (can be zero).\n * @param manufacturerCutBP Manufacturer cut in basis points (e.g., 400 = 4%).\n * @param partnerCutBP Partner cut in basis points (e.g., 300 = 3%).\n * @param defaultCreatorCutBP Default creator cut in basis points (e.g., 300 = 3%).\n * @param defaultCreatorWallet Fallback wallet for unregistered creators.\n * @return proxy The deployed collection proxy address.\n * @return splitter The deployed splitter address (zero if not deployed).\n */\n function deployCloneWithSplitter(\n bytes calldata initCalldata,\n bool deploySplitter,\n address partnerWallet,\n uint96 manufacturerCutBP,\n uint96 partnerCutBP,\n uint96 defaultCreatorCutBP,\n address defaultCreatorWallet\n ) external returns (address proxy, address splitter) {\n if (!publicDeployment) {\n _checkRole(CLONE_DEPLOYER_ROLE, msg.sender);\n }\n\n // 1. Deploy the NFT collection clone\n proxy = address(new BeaconProxy(address(beacon), initCalldata));\n\n // 2. Configure marketplace security (whitelist Seaport & conduit, restrict other markets)\n _configureMarketplaceSecurity(proxy);\n\n if (deploySplitter) {\n // 3. Get manufacturer wallet from collection's first DEFAULT_ADMIN_ROLE member\n address manufacturerWallet = _getCollectionManufacturer(proxy);\n\n // 4. Deploy splitter with factory as the admin\n splitter = address(new KonduxRoyaltySplitter(\n proxy, // collection\n manufacturerWallet, // manufacturer wallet\n partnerWallet, // partner wallet\n manufacturerCutBP,\n partnerCutBP,\n defaultCreatorCutBP,\n defaultCreatorWallet, // fallback wallet for unregistered creators\n address(this) // Factory is initial admin\n ));\n\n // 5. Grant COLLECTION_ROLE to the NFT contract\n IKonduxRoyaltySplitter(splitter).grantRole(\n IKonduxRoyaltySplitter(splitter).COLLECTION_ROLE(),\n proxy\n );\n\n // 6. Configure the NFT collection to use the splitter\n // Note: This requires the collection to have setRoyaltySplitter function\n (bool success,) = proxy.call(\n abi.encodeWithSignature(\"setRoyaltySplitter(address)\", splitter)\n );\n require(success, \"Failed to set splitter on collection\");\n\n // 7. Track the deployment\n collectionToSplitter[proxy] = splitter;\n deployedSplitters.push(splitter);\n\n emit SplitterDeployed(proxy, splitter, msg.sender);\n }\n\n emit CloneDeployed(proxy, msg.sender);\n }\n\n /**\n * @dev Configures default marketplace security on a newly deployed collection.\n * Creates a whitelist with Seaport 1.6, applies Operator Whitelist mode (level 4),\n * then adds the OpenSea conduit to the whitelist.\n * If the transfer validator is not set on the collection, this is a no-op.\n * @param collection The collection address to configure.\n */\n function _configureMarketplaceSecurity(address collection) internal {\n // 1. Set default security policy (whitelists Seaport, sets level 4)\n // Note: This will fail silently if transfer validator is not set on the collection\n (bool success1,) = collection.call(\n abi.encodeWithSignature(\"setToDefaultSecurityPolicy()\")\n );\n\n // 2. Only add conduit to whitelist if security policy was successfully set\n if (success1) {\n address[] memory conduit = new address[](1);\n conduit[0] = OPENSEA_CONDUIT;\n (bool success2,) = collection.call(\n abi.encodeWithSignature(\"addAccountsToWhitelist(address[])\", conduit)\n );\n // Emit event if security was configured\n if (success2) {\n emit MarketplaceSecurityConfigured(collection);\n }\n }\n }\n\n /**\n * @dev Reads the first DEFAULT_ADMIN_ROLE member from a deployed collection.\n */\n function _getCollectionManufacturer(address collection) internal view returns (address) {\n // DEFAULT_ADMIN_ROLE is always bytes32(0) in OpenZeppelin AccessControl\n bytes32 adminRole = 0x00;\n uint256 memberCount = IAccessControlEnumerable(collection).getRoleMemberCount(adminRole);\n require(memberCount > 0, \"Factory: collection has no admin\");\n return IAccessControlEnumerable(collection).getRoleMember(adminRole, 0);\n }\n\n /*-------------------------------------------------------------*/\n /* FEE_ADMIN functions */\n /*-------------------------------------------------------------*/\n\n /**\n * @notice Allows FEE_ADMIN to update cuts on any factory-deployed splitter.\n * @param splitter The splitter address.\n * @param _manufacturerCutBP New manufacturer cut.\n * @param _partnerCutBP New partner cut.\n * @param _defaultCreatorCutBP New default creator cut.\n */\n function setCutsOnSplitter(\n address splitter,\n uint96 _manufacturerCutBP,\n uint96 _partnerCutBP,\n uint96 _defaultCreatorCutBP\n ) external onlyRole(FEE_ADMIN_ROLE) {\n IKonduxRoyaltySplitter(splitter).setCuts(\n _manufacturerCutBP,\n _partnerCutBP,\n _defaultCreatorCutBP\n );\n }\n\n /**\n * @notice Allows FEE_ADMIN to update wallets on any factory-deployed splitter.\n * @param splitter The splitter address.\n * @param manufacturerWallet New manufacturer wallet.\n * @param partnerWallet New partner wallet.\n */\n function setWalletsOnSplitter(\n address splitter,\n address manufacturerWallet,\n address partnerWallet\n ) external onlyRole(FEE_ADMIN_ROLE) {\n IKonduxRoyaltySplitter(splitter).setWallets(manufacturerWallet, partnerWallet);\n }\n\n /**\n * @notice Register or override a creator on a splitter.\n * @param splitter The splitter address.\n * @param tokenId The token ID.\n * @param creator The creator address.\n * @param creatorCutBP The creator's cut in basis points.\n */\n function registerCreatorOnSplitter(\n address splitter,\n uint256 tokenId,\n address creator,\n uint96 creatorCutBP\n ) external onlyRole(FEE_ADMIN_ROLE) {\n IKonduxRoyaltySplitter(splitter).overrideCreator(tokenId, creator, creatorCutBP);\n }\n\n /**\n * @notice Grant a role on a splitter to an account.\n * @dev Only callable by DEFAULT_ADMIN_ROLE.\n * @param splitter The splitter contract address.\n * @param role The role to grant.\n * @param account The account to receive the role.\n */\n function grantSplitterRole(\n address splitter,\n bytes32 role,\n address account\n ) external onlyRole(DEFAULT_ADMIN_ROLE) {\n IKonduxRoyaltySplitter(splitter).grantRole(role, account);\n }\n\n /**\n * @notice Sweep accumulated ETH from a splitter.\n * @dev Only callable by FEE_ADMIN_ROLE.\n * @param splitter The splitter contract address.\n * @param tokenId The token ID to attribute the sweep to.\n */\n function sweepSplitterETH(\n address splitter,\n uint256 tokenId\n ) external onlyRole(FEE_ADMIN_ROLE) {\n // This requires the factory to have ADMIN_ROLE on the splitter\n (bool success, bytes memory data) = splitter.call(\n abi.encodeWithSignature(\"sweepETH(uint256)\", tokenId)\n );\n require(success, string(data));\n }\n\n /**\n * @notice Returns the number of deployed splitters.\n */\n function deployedSplittersCount() external view returns (uint256) {\n return deployedSplitters.length;\n }\n\n /*-------------------------------------------------------------*/\n /* Events */\n /*-------------------------------------------------------------*/\n event GlobalUpgrade(address newImplementation);\n event CloneDeployed(address proxy, address indexed creator);\n event PublicDeploymentChanged(bool open);\n event SplitterDeployed(address indexed collection, address indexed splitter, address indexed creator);\n event MarketplaceSecurityConfigured(address indexed collection);\n}\n"},"node_modules/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/UpgradeableBeacon.sol)\n\npragma solidity ^0.8.20;\n\nimport {IBeacon} from \"./IBeacon.sol\";\nimport {Ownable} from \"../../access/Ownable.sol\";\n\n/**\n * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their\n * implementation contract, which is where they will delegate all function calls.\n *\n * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\n */\ncontract UpgradeableBeacon is IBeacon, Ownable {\n address private _implementation;\n\n /**\n * @dev The `implementation` of the beacon is invalid.\n */\n error BeaconInvalidImplementation(address implementation);\n\n /**\n * @dev Emitted when the implementation returned by the beacon is changed.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Sets the address of the initial implementation, and the initial owner who can upgrade the beacon.\n */\n constructor(address implementation_, address initialOwner) Ownable(initialOwner) {\n _setImplementation(implementation_);\n }\n\n /**\n * @dev Returns the current implementation address.\n */\n function implementation() public view virtual returns (address) {\n return _implementation;\n }\n\n /**\n * @dev Upgrades the beacon to a new implementation.\n *\n * Emits an {Upgraded} event.\n *\n * Requirements:\n *\n * - msg.sender must be the owner of the contract.\n * - `newImplementation` must be a contract.\n */\n function upgradeTo(address newImplementation) public virtual onlyOwner {\n _setImplementation(newImplementation);\n }\n\n /**\n * @dev Sets the implementation contract address for this beacon\n *\n * Requirements:\n *\n * - `newImplementation` must be a contract.\n */\n function _setImplementation(address newImplementation) private {\n if (newImplementation.code.length == 0) {\n revert BeaconInvalidImplementation(newImplementation);\n }\n _implementation = newImplementation;\n emit Upgraded(newImplementation);\n }\n}\n"},"node_modules/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.2.0) (proxy/beacon/BeaconProxy.sol)\n\npragma solidity ^0.8.22;\n\nimport {IBeacon} from \"./IBeacon.sol\";\nimport {Proxy} from \"../Proxy.sol\";\nimport {ERC1967Utils} from \"../ERC1967/ERC1967Utils.sol\";\n\n/**\n * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.\n *\n * The beacon address can only be set once during construction, and cannot be changed afterwards. It is stored in an\n * immutable variable to avoid unnecessary storage reads, and also in the beacon storage slot specified by\n * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] so that it can be accessed externally.\n *\n * CAUTION: Since the beacon address can never be changed, you must ensure that you either control the beacon, or trust\n * the beacon to not upgrade the implementation maliciously.\n *\n * IMPORTANT: Do not use the implementation logic to modify the beacon storage slot. Doing so would leave the proxy in\n * an inconsistent state where the beacon storage slot does not match the beacon address.\n */\ncontract BeaconProxy is Proxy {\n // An immutable address for the beacon to avoid unnecessary SLOADs before each delegate call.\n address private immutable _beacon;\n\n /**\n * @dev Initializes the proxy with `beacon`.\n *\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This\n * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity\n * constructor.\n *\n * Requirements:\n *\n * - `beacon` must be a contract with the interface {IBeacon}.\n * - If `data` is empty, `msg.value` must be zero.\n */\n constructor(address beacon, bytes memory data) payable {\n ERC1967Utils.upgradeBeaconToAndCall(beacon, data);\n _beacon = beacon;\n }\n\n /**\n * @dev Returns the current implementation address of the associated beacon.\n */\n function _implementation() internal view virtual override returns (address) {\n return IBeacon(_getBeacon()).implementation();\n }\n\n /**\n * @dev Returns the beacon.\n */\n function _getBeacon() internal view virtual returns (address) {\n return _beacon;\n }\n}\n"},"node_modules/@openzeppelin/contracts/access/AccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {IERC165, ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address account => bool) hasRole;\n bytes32 adminRole;\n }\n\n mapping(bytes32 role => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with an {AccessControlUnauthorizedAccount} error including the required role.\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role);\n _;\n }\n\n /// @inheritdoc IERC165\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n return _roles[role].hasRole[account];\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n */\n function _checkRole(bytes32 role) internal view virtual {\n _checkRole(role, _msgSender());\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n * is missing `role`.\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert AccessControlUnauthorizedAccount(account, role);\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleGranted} event.\n */\n function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleRevoked} event.\n */\n function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n *\n * May emit a {RoleRevoked} event.\n */\n function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n if (callerConfirmation != _msgSender()) {\n revert AccessControlBadConfirmation();\n }\n\n _revokeRole(role, callerConfirmation);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleGranted} event.\n */\n function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n if (!hasRole(role, account)) {\n _roles[role].hasRole[account] = true;\n emit RoleGranted(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleRevoked} event.\n */\n function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n if (hasRole(role, account)) {\n _roles[role].hasRole[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n}\n"},"node_modules/@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (access/extensions/IAccessControlEnumerable.sol)\n\npragma solidity >=0.8.4;\n\nimport {IAccessControl} from \"../IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n"},"contracts/KonduxRoyaltySplitter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\n\n/**\n * @title KonduxRoyaltySplitter\n * @notice Receives royalty payments from marketplaces and distributes them according to\n * the Kondux royalty model:\n *\n * - Manufacturer cut (m): Fixed %, goes to Kondux treasury\n * - Partner cut (p): Fixed %, goes to partner/collection owner\n * - Creator cut (c): Variable %, goes to the original minter of the NFT\n *\n * Creator receives their cut on ALL sales (including first sale).\n * OpenSea sees a CONSTANT total royalty (m + p + max_c) going to this contract.\n * This contract then distributes based on per-token creator data.\n *\n * Supports two modes:\n * 1. PULL MODE: Royalties accumulate, recipients withdraw later\n * 2. PUSH MODE: Atomic distribution during transfer (via onTransferWithValue)\n *\n * @dev This contract should be set as the ERC2981 royalty receiver for the collection.\n */\ncontract KonduxRoyaltySplitter is AccessControl, ReentrancyGuard {\n using SafeERC20 for IERC20;\n\n bytes32 public constant ADMIN_ROLE = keccak256(\"ADMIN_ROLE\");\n bytes32 public constant DISTRIBUTOR_ROLE = keccak256(\"DISTRIBUTOR_ROLE\");\n bytes32 public constant COLLECTION_ROLE = keccak256(\"COLLECTION_ROLE\");\n bytes32 public constant FEE_ADMIN_ROLE = keccak256(\"FEE_ADMIN_ROLE\");\n\n /// @notice The NFT collection this splitter serves\n address public immutable collection;\n \n /// @notice Manufacturer (Kondux) treasury address\n address public manufacturerWallet;\n \n /// @notice Partner/collection owner address \n address public partnerWallet;\n \n /// @notice Basis points for manufacturer (e.g., 400 = 4%)\n uint96 public manufacturerCutBP;\n \n /// @notice Basis points for partner (e.g., 300 = 3%)\n uint96 public partnerCutBP;\n \n /// @notice Default creator cut for tokens without specific override (e.g., 300 = 3%)\n uint96 public defaultCreatorCutBP;\n\n /// @notice Default creator wallet for tokens without registered creator\n address public defaultCreatorWallet;\n\n /// @notice Maximum total royalty (what OpenSea sees as creator_fee)\n uint96 public constant MAX_TOTAL_ROYALTY_BP = 1000; // 10%\n \n uint96 public constant DENOMINATOR = 10000;\n\n /// @notice Whether to use push (immediate) distribution mode\n bool public pushModeEnabled;\n\n /// @notice Per-token creator info\n struct CreatorInfo {\n address creator; // The original minter/creator\n uint96 creatorCutBP; // Their specific cut in basis points\n }\n \n mapping(uint256 => CreatorInfo) public tokenCreators;\n \n /// @notice Accumulated balances for each recipient\n mapping(address => uint256) public pendingETH;\n mapping(address => mapping(address => uint256)) public pendingERC20; // token => recipient => amount\n\n /// @notice Events\n event RoyaltyReceived(uint256 indexed tokenId, uint256 amount, address indexed token);\n event RoyaltyDistributed(\n uint256 indexed tokenId,\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address indexed creator\n );\n event CreatorRegistered(uint256 indexed tokenId, address indexed creator, uint96 cutBP);\n event WalletsUpdated(address manufacturer, address partner);\n event CutsUpdated(uint96 manufacturerBP, uint96 partnerBP, uint96 defaultCreatorBP);\n event Withdrawn(address indexed recipient, uint256 amount, address indexed token);\n event PushModeUpdated(bool enabled);\n event ImmediateDistribution(\n uint256 indexed tokenId,\n uint256 salePrice,\n uint256 royaltyAmount,\n address indexed seller\n );\n event CreatorWalletUpdated(uint256 indexed tokenId, address indexed oldWallet, address indexed newWallet);\n event CreatorOverridden(uint256 indexed tokenId, address indexed creator, uint96 cutBP);\n event DefaultCreatorWalletUpdated(address indexed newDefaultCreator);\n\n error InvalidAddress();\n error NotCreatorOfToken();\n error AlreadyRegistered();\n error InvalidCuts();\n error TokenNotRegistered();\n error NoFundsToWithdraw();\n error OnlyCollection();\n error TransferFailed();\n\n constructor(\n address _collection,\n address _manufacturerWallet,\n address _partnerWallet,\n uint96 _manufacturerCutBP,\n uint96 _partnerCutBP,\n uint96 _defaultCreatorCutBP,\n address _defaultCreatorWallet,\n address _admin\n ) {\n if (_collection == address(0) || _manufacturerWallet == address(0) || _admin == address(0)) {\n revert InvalidAddress();\n }\n if (_manufacturerCutBP + _partnerCutBP + _defaultCreatorCutBP > MAX_TOTAL_ROYALTY_BP) {\n revert InvalidCuts();\n }\n\n collection = _collection;\n manufacturerWallet = _manufacturerWallet;\n partnerWallet = _partnerWallet;\n manufacturerCutBP = _manufacturerCutBP;\n partnerCutBP = _partnerCutBP;\n defaultCreatorCutBP = _defaultCreatorCutBP;\n defaultCreatorWallet = _defaultCreatorWallet;\n\n // Enable push mode by default\n pushModeEnabled = true;\n\n _grantRole(DEFAULT_ADMIN_ROLE, _admin);\n _grantRole(ADMIN_ROLE, _admin);\n _grantRole(FEE_ADMIN_ROLE, _admin);\n _grantRole(DISTRIBUTOR_ROLE, _admin);\n _grantRole(COLLECTION_ROLE, _collection);\n }\n\n /// @notice Receive ETH (royalty payments)\n /// @dev Uses bidirectional same-block matching to handle Seaport execution order\n receive() external payable {\n if (!pushModeEnabled || msg.value == 0) return;\n\n // BIDIRECTIONAL MATCHING: Check if a sale was registered in this same block\n // Note: pendingSaleBlock != 0 indicates a pending sale exists (tokenId can be 0)\n if (pendingSaleBlock == block.number && pendingSaleBlock != 0) {\n // Sale was registered first in this block - distribute now\n uint256 tokenId = pendingSaleTokenId;\n uint256 amount = msg.value;\n\n // Clear state BEFORE distribution (reentrancy protection)\n _clearPendingSaleState();\n\n _distributeETHImmediate(tokenId, amount);\n emit SameBlockDistribution(tokenId, amount, false); // ethFirst=false, sale came first\n return; // Exit after distribution\n }\n\n // No matching sale in this block yet - record ETH for later matching\n // If registerSale() is called later in this same block, it will distribute\n pendingETHAmount = msg.value;\n pendingETHBlock = block.number;\n }\n\n /// @notice Track pending sales for atomic distribution\n /// @dev Maps a unique sale ID to the tokenId being sold\n mapping(bytes32 => uint256) public pendingSales;\n \n /// @notice Counter for generating unique sale IDs\n uint256 public saleNonce;\n\n /// @notice Last sold token ID for automatic distribution in receive()\n /// @dev Set during registerSale, used by receive() to know which token's royalty is being paid\n uint256 public lastSoldTokenId;\n\n /// @notice Whether there's a pending sale awaiting payment\n bool public hasPendingSale;\n\n /*//////////////////////////////////////////////////////////////\n BIDIRECTIONAL SAME-BLOCK MATCHING\n //////////////////////////////////////////////////////////////*/\n\n /// @notice Pending ETH amount waiting to be matched with a sale\n uint256 public pendingETHAmount;\n\n /// @notice Block number when pending ETH was received\n uint256 public pendingETHBlock;\n\n /// @notice Token ID of pending sale waiting to be matched with ETH\n uint256 public pendingSaleTokenId;\n\n /// @notice Block number when pending sale was registered\n uint256 public pendingSaleBlock;\n\n /// @notice Emitted when ETH and sale are matched in the same block\n event SameBlockDistribution(uint256 indexed tokenId, uint256 amount, bool ethFirst);\n\n /// @dev Clear pending sale state after distribution\n function _clearPendingSaleState() internal {\n pendingSaleTokenId = 0;\n pendingSaleBlock = 0;\n // Also clear legacy state\n hasPendingSale = false;\n lastSoldTokenId = 0;\n }\n\n /// @dev Clear pending ETH state after distribution\n function _clearPendingETHState() internal {\n pendingETHAmount = 0;\n pendingETHBlock = 0;\n }\n\n /**\n * @notice Called by the collection contract BEFORE the transfer to register a pending sale\n * @param tokenId The token about to be sold\n * @return saleId A unique identifier for this sale (used to match with incoming payment)\n * @dev Uses bidirectional same-block matching to handle Seaport execution order\n */\n function registerSale(uint256 tokenId) external onlyRole(COLLECTION_ROLE) returns (bytes32 saleId) {\n saleId = keccak256(abi.encodePacked(block.timestamp, tokenId, saleNonce++));\n pendingSales[saleId] = tokenId;\n\n // BIDIRECTIONAL MATCHING: Check if ETH was received in this same block\n if (pendingETHBlock == block.number && pendingETHAmount > 0) {\n // ETH arrived first in this block - distribute now\n uint256 amount = pendingETHAmount;\n\n // Clear state BEFORE distribution (reentrancy protection)\n _clearPendingETHState();\n\n _distributeETHImmediate(tokenId, amount);\n emit SameBlockDistribution(tokenId, amount, true); // ethFirst=true\n } else {\n // No matching ETH in this block yet - record sale for later matching\n // If receive() is called later in this same block, it will distribute\n pendingSaleTokenId = tokenId;\n pendingSaleBlock = block.number;\n\n // Also set legacy state for backwards compatibility\n lastSoldTokenId = tokenId;\n hasPendingSale = true;\n }\n\n emit SaleRegistered(saleId, tokenId);\n }\n\n /**\n * @notice Receive payment for a specific sale and distribute immediately\n * @param saleId The unique sale identifier from registerSale()\n * @dev Called by marketplace or wrapper contract with the royalty payment\n */\n function receivePaymentForSale(bytes32 saleId) external payable nonReentrant {\n uint256 tokenId = pendingSales[saleId];\n require(tokenId != 0 || saleId == bytes32(0), \"Unknown sale\");\n \n if (msg.value > 0) {\n if (pushModeEnabled && tokenId != 0) {\n _distributeETHImmediate(tokenId, msg.value);\n } else {\n _distributeETH(tokenId, msg.value);\n }\n }\n \n // Clear the pending sale\n delete pendingSales[saleId];\n }\n\n /**\n * @notice Alternative: Receive payment with tokenId directly specified\n * @param tokenId The token that was sold\n * @dev Simpler approach - caller must be trusted (COLLECTION_ROLE or DISTRIBUTOR_ROLE)\n */\n function receivePaymentForToken(uint256 tokenId) external payable nonReentrant {\n require(\n hasRole(COLLECTION_ROLE, msg.sender) || hasRole(DISTRIBUTOR_ROLE, msg.sender),\n \"Not authorized\"\n );\n \n if (msg.value > 0) {\n if (pushModeEnabled) {\n _distributeETHImmediate(tokenId, msg.value);\n } else {\n _distributeETH(tokenId, msg.value);\n }\n }\n }\n\n event SaleRegistered(bytes32 indexed saleId, uint256 indexed tokenId);\n\n /*//////////////////////////////////////////////////////////////\n TRANSFER HOOK (ATOMIC)\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Called by the NFT contract during a transfer to atomically distribute royalties.\n * This enables \"push mode\" where royalties are paid immediately during the sale.\n * @param tokenId The token being transferred\n * @param from The seller\n * @param salePrice The sale price (msg.value sent to this function)\n * @dev Only callable by the collection contract. Expects ETH to be sent with the call.\n */\n function onTransferWithValue(\n uint256 tokenId,\n address from,\n uint256 salePrice\n ) external payable onlyRole(COLLECTION_ROLE) nonReentrant {\n if (msg.value == 0) return;\n \n // Calculate royalty based on sale price\n uint256 royaltyAmount = (salePrice * MAX_TOTAL_ROYALTY_BP) / DENOMINATOR;\n \n // Use actual received value (may differ due to rounding)\n uint256 actualAmount = msg.value;\n if (actualAmount > royaltyAmount) {\n actualAmount = royaltyAmount;\n }\n\n if (pushModeEnabled) {\n // Immediate distribution\n _distributeETHImmediate(tokenId, actualAmount);\n } else {\n // Accumulate for later withdrawal\n _distributeETH(tokenId, actualAmount);\n }\n\n emit ImmediateDistribution(tokenId, salePrice, actualAmount, from);\n }\n\n /**\n * @notice Immediately distribute ETH to recipients (no accumulation)\n */\n function _distributeETHImmediate(uint256 tokenId, uint256 amount) internal {\n (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator\n ) = _calculateSplit(tokenId, amount);\n\n // Immediate transfers\n if (manufacturerAmount > 0) {\n _safeTransferETH(manufacturerWallet, manufacturerAmount);\n }\n \n if (partnerWallet != address(0) && partnerAmount > 0) {\n _safeTransferETH(partnerWallet, partnerAmount);\n } else if (partnerAmount > 0) {\n _safeTransferETH(manufacturerWallet, partnerAmount);\n }\n \n if (creator != address(0) && creatorAmount > 0) {\n _safeTransferETH(creator, creatorAmount);\n } else if (creatorAmount > 0) {\n _safeTransferETH(manufacturerWallet, creatorAmount);\n }\n\n emit RoyaltyDistributed(tokenId, manufacturerAmount, partnerAmount, creatorAmount, creator);\n }\n\n /**\n * @notice Safe ETH transfer with fallback to pending balance\n */\n function _safeTransferETH(address to, uint256 amount) internal {\n (bool success, ) = to.call{value: amount, gas: 50000}(\"\");\n if (!success) {\n // If transfer fails (e.g., contract without receive), accumulate instead\n pendingETH[to] += amount;\n }\n }\n\n /**\n * @notice Enable or disable push (immediate) distribution mode\n */\n function setPushMode(bool enabled) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) || hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized\"\n );\n pushModeEnabled = enabled;\n emit PushModeUpdated(enabled);\n }\n\n /*//////////////////////////////////////////////////////////////\n ADMIN FUNCTIONS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Register creator info for a token (called by collection on mint or by admin)\n * @param tokenId The token ID\n * @param creator The creator's address\n * @param creatorCutBP The creator's royalty cut in basis points\n * @dev COLLECTION_ROLE can only register new tokens. Admins can override existing.\n */\n function registerCreator(\n uint256 tokenId,\n address creator,\n uint96 creatorCutBP\n ) external {\n // Check authorization\n bool isAdmin = hasRole(ADMIN_ROLE, msg.sender) ||\n hasRole(DISTRIBUTOR_ROLE, msg.sender) ||\n hasRole(FEE_ADMIN_ROLE, msg.sender);\n bool isCollection = hasRole(COLLECTION_ROLE, msg.sender);\n\n require(isAdmin || isCollection, \"Not authorized\");\n\n if (creator == address(0)) revert InvalidAddress();\n if (manufacturerCutBP + partnerCutBP + creatorCutBP > MAX_TOTAL_ROYALTY_BP) {\n revert InvalidCuts();\n }\n\n // If already registered, only admins can override (not collection)\n if (tokenCreators[tokenId].creator != address(0)) {\n require(isAdmin, \"Already registered, need admin to override\");\n }\n\n tokenCreators[tokenId] = CreatorInfo({\n creator: creator,\n creatorCutBP: creatorCutBP\n });\n\n emit CreatorRegistered(tokenId, creator, creatorCutBP);\n }\n\n /**\n * @notice Batch register creators (gas efficient for minting batches)\n * @dev Only admins can batch register (not collection) to prevent abuse\n */\n function registerCreatorsBatch(\n uint256[] calldata tokenIds,\n address[] calldata creators,\n uint96[] calldata creatorCutsBP\n ) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) ||\n hasRole(DISTRIBUTOR_ROLE, msg.sender) ||\n hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized\"\n );\n require(\n tokenIds.length == creators.length && creators.length == creatorCutsBP.length,\n \"Length mismatch\"\n );\n\n for (uint256 i = 0; i < tokenIds.length; i++) {\n if (creators[i] == address(0)) revert InvalidAddress();\n if (manufacturerCutBP + partnerCutBP + creatorCutsBP[i] > MAX_TOTAL_ROYALTY_BP) {\n revert InvalidCuts();\n }\n\n tokenCreators[tokenIds[i]] = CreatorInfo({\n creator: creators[i],\n creatorCutBP: creatorCutsBP[i]\n });\n\n emit CreatorRegistered(tokenIds[i], creators[i], creatorCutsBP[i]);\n }\n }\n\n /**\n * @notice Update wallet addresses\n */\n function setWallets(\n address _manufacturerWallet,\n address _partnerWallet\n ) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) || hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized\"\n );\n if (_manufacturerWallet == address(0)) revert InvalidAddress();\n manufacturerWallet = _manufacturerWallet;\n partnerWallet = _partnerWallet;\n emit WalletsUpdated(_manufacturerWallet, _partnerWallet);\n }\n\n /**\n * @notice Update cut percentages\n */\n function setCuts(\n uint96 _manufacturerCutBP,\n uint96 _partnerCutBP,\n uint96 _defaultCreatorCutBP\n ) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) || hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized\"\n );\n if (_manufacturerCutBP + _partnerCutBP + _defaultCreatorCutBP > MAX_TOTAL_ROYALTY_BP) {\n revert InvalidCuts();\n }\n manufacturerCutBP = _manufacturerCutBP;\n partnerCutBP = _partnerCutBP;\n defaultCreatorCutBP = _defaultCreatorCutBP;\n emit CutsUpdated(_manufacturerCutBP, _partnerCutBP, _defaultCreatorCutBP);\n }\n\n /**\n * @notice Set the default creator wallet (used when no creator is registered for a token)\n * @param _defaultCreatorWallet The fallback creator wallet address\n */\n function setDefaultCreatorWallet(address _defaultCreatorWallet) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) || hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized\"\n );\n defaultCreatorWallet = _defaultCreatorWallet;\n emit DefaultCreatorWalletUpdated(_defaultCreatorWallet);\n }\n\n /**\n * @notice Allows a registered creator to change their own receiving wallet\n * @param tokenId The token ID for which they are the creator\n * @param newWallet The new wallet address to receive royalties\n */\n function updateCreatorWallet(uint256 tokenId, address newWallet) external {\n CreatorInfo storage info = tokenCreators[tokenId];\n if (info.creator != msg.sender) revert NotCreatorOfToken();\n if (newWallet == address(0)) revert InvalidAddress();\n\n address oldWallet = info.creator;\n info.creator = newWallet;\n\n emit CreatorWalletUpdated(tokenId, oldWallet, newWallet);\n }\n\n /**\n * @notice Admin/Distributor can override creator registration\n * @param tokenId The token ID\n * @param creator The new creator address\n * @param creatorCutBP The creator's royalty cut in basis points\n */\n function overrideCreator(\n uint256 tokenId,\n address creator,\n uint96 creatorCutBP\n ) external {\n require(\n hasRole(ADMIN_ROLE, msg.sender) ||\n hasRole(DISTRIBUTOR_ROLE, msg.sender) ||\n hasRole(FEE_ADMIN_ROLE, msg.sender),\n \"Not authorized to override\"\n );\n\n if (creator == address(0)) revert InvalidAddress();\n if (manufacturerCutBP + partnerCutBP + creatorCutBP > MAX_TOTAL_ROYALTY_BP) {\n revert InvalidCuts();\n }\n\n tokenCreators[tokenId] = CreatorInfo({\n creator: creator,\n creatorCutBP: creatorCutBP\n });\n\n emit CreatorOverridden(tokenId, creator, creatorCutBP);\n }\n\n /*//////////////////////////////////////////////////////////////\n DISTRIBUTION FUNCTIONS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Distribute ETH royalty for a specific token sale\n * @param tokenId The token that was sold\n * @param amount The royalty amount to distribute\n */\n function distributeETH(uint256 tokenId, uint256 amount) external nonReentrant {\n _distributeETH(tokenId, amount);\n }\n\n /**\n * @notice Distribute ERC20 royalty for a specific token sale\n * @param tokenId The token that was sold\n * @param token The ERC20 token address\n * @param amount The royalty amount to distribute\n */\n function distributeERC20(\n uint256 tokenId,\n address token,\n uint256 amount\n ) external nonReentrant {\n _distributeERC20(tokenId, token, amount);\n }\n\n /**\n * @notice Internal ETH distribution logic\n */\n function _distributeETH(uint256 tokenId, uint256 amount) internal {\n (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator\n ) = _calculateSplit(tokenId, amount);\n\n // Accumulate pending balances\n pendingETH[manufacturerWallet] += manufacturerAmount;\n if (partnerWallet != address(0) && partnerAmount > 0) {\n pendingETH[partnerWallet] += partnerAmount;\n } else {\n // If no partner, manufacturer gets their share too\n pendingETH[manufacturerWallet] += partnerAmount;\n }\n if (creator != address(0) && creatorAmount > 0) {\n pendingETH[creator] += creatorAmount;\n } else {\n // If no creator, manufacturer gets their share\n pendingETH[manufacturerWallet] += creatorAmount;\n }\n\n emit RoyaltyDistributed(tokenId, manufacturerAmount, partnerAmount, creatorAmount, creator);\n }\n\n /**\n * @notice Internal ERC20 distribution logic\n */\n function _distributeERC20(uint256 tokenId, address token, uint256 amount) internal {\n (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator\n ) = _calculateSplit(tokenId, amount);\n\n pendingERC20[token][manufacturerWallet] += manufacturerAmount;\n if (partnerWallet != address(0) && partnerAmount > 0) {\n pendingERC20[token][partnerWallet] += partnerAmount;\n } else {\n pendingERC20[token][manufacturerWallet] += partnerAmount;\n }\n if (creator != address(0) && creatorAmount > 0) {\n pendingERC20[token][creator] += creatorAmount;\n } else {\n pendingERC20[token][manufacturerWallet] += creatorAmount;\n }\n\n emit RoyaltyDistributed(tokenId, manufacturerAmount, partnerAmount, creatorAmount, creator);\n }\n\n /**\n * @notice Calculate the split for a given token and amount\n * @dev Creator receives their cut on ALL sales (no first sale distinction)\n */\n function _calculateSplit(uint256 tokenId, uint256 amount)\n internal\n view\n returns (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator\n )\n {\n CreatorInfo memory info = tokenCreators[tokenId];\n bool hasCreator = info.creator != address(0);\n\n // Determine creator cut and address\n // If no creator registered, fallback to defaultCreatorWallet with defaultCreatorCutBP\n if (hasCreator) {\n creator = info.creator;\n } else if (defaultCreatorWallet != address(0)) {\n creator = defaultCreatorWallet;\n } else {\n creator = address(0);\n }\n\n uint96 actualCreatorCutBP = hasCreator ? info.creatorCutBP : defaultCreatorCutBP;\n\n // Calculate amounts\n uint256 totalBP = manufacturerCutBP + partnerCutBP + actualCreatorCutBP;\n\n if (totalBP == 0) {\n // No splits configured, everything to manufacturer\n return (amount, 0, 0, address(0));\n }\n\n // Proportional distribution based on the ACTUAL cuts (not max)\n // The received amount is based on MAX_TOTAL_ROYALTY_BP from OpenSea\n // We distribute based on actual cuts, remainder goes to manufacturer\n\n manufacturerAmount = (amount * manufacturerCutBP) / MAX_TOTAL_ROYALTY_BP;\n partnerAmount = (amount * partnerCutBP) / MAX_TOTAL_ROYALTY_BP;\n creatorAmount = (amount * actualCreatorCutBP) / MAX_TOTAL_ROYALTY_BP;\n\n // Any remainder (from tokens with lower creator cut) goes to manufacturer\n uint256 distributed = manufacturerAmount + partnerAmount + creatorAmount;\n if (distributed < amount) {\n manufacturerAmount += (amount - distributed);\n }\n }\n\n /**\n * @notice Get the split breakdown for a token (view function for frontends)\n */\n function getSplit(uint256 tokenId, uint256 amount)\n external\n view\n returns (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator,\n uint96 creatorCutBP\n )\n {\n CreatorInfo memory info = tokenCreators[tokenId];\n bool hasCreator = info.creator != address(0);\n creatorCutBP = hasCreator ? info.creatorCutBP : defaultCreatorCutBP;\n\n (manufacturerAmount, partnerAmount, creatorAmount, creator) = _calculateSplit(tokenId, amount);\n }\n\n /*//////////////////////////////////////////////////////////////\n WITHDRAWAL FUNCTIONS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Withdraw accumulated ETH\n */\n function withdrawETH() external nonReentrant {\n uint256 amount = pendingETH[msg.sender];\n if (amount == 0) revert NoFundsToWithdraw();\n \n pendingETH[msg.sender] = 0;\n \n (bool success, ) = msg.sender.call{value: amount}(\"\");\n require(success, \"ETH transfer failed\");\n \n emit Withdrawn(msg.sender, amount, address(0));\n }\n\n /**\n * @notice Withdraw accumulated ERC20 tokens\n */\n function withdrawERC20(address token) external nonReentrant {\n uint256 amount = pendingERC20[token][msg.sender];\n if (amount == 0) revert NoFundsToWithdraw();\n \n pendingERC20[token][msg.sender] = 0;\n \n IERC20(token).safeTransfer(msg.sender, amount);\n \n emit Withdrawn(msg.sender, amount, token);\n }\n\n /**\n * @notice Admin function to distribute all pending ETH in contract\n * Useful if royalties were sent without calling distribute()\n */\n function sweepETH(uint256 tokenId) external onlyRole(ADMIN_ROLE) nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n _distributeETH(tokenId, balance);\n }\n }\n\n /**\n * @notice Admin function to distribute all pending ERC20 in contract\n */\n function sweepERC20(uint256 tokenId, address token) external onlyRole(ADMIN_ROLE) nonReentrant {\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n _distributeERC20(tokenId, token, balance);\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n VIEW FUNCTIONS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Check if a token has creator info registered\n */\n function hasCreatorInfo(uint256 tokenId) external view returns (bool) {\n return tokenCreators[tokenId].creator != address(0);\n }\n\n /**\n * @notice Get creator info for a token\n */\n function getCreatorInfo(uint256 tokenId)\n external\n view\n returns (address creator, uint96 cutBP)\n {\n CreatorInfo memory info = tokenCreators[tokenId];\n return (info.creator, info.creatorCutBP);\n }\n\n /**\n * @notice Get total pending ETH for an address\n */\n function getPendingETH(address recipient) external view returns (uint256) {\n return pendingETH[recipient];\n }\n\n /**\n * @notice Get total pending ERC20 for an address\n */\n function getPendingERC20(address token, address recipient) external view returns (uint256) {\n return pendingERC20[token][recipient];\n }\n}\n"},"contracts/interfaces/IKonduxRoyaltySplitter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/**\n * @title IKonduxRoyaltySplitter\n * @notice Interface for the Kondux royalty splitter contract\n */\ninterface IKonduxRoyaltySplitter {\n /*//////////////////////////////////////////////////////////////\n ROLE CONSTANTS\n //////////////////////////////////////////////////////////////*/\n\n /// @notice Returns the ADMIN_ROLE identifier\n function ADMIN_ROLE() external view returns (bytes32);\n\n /// @notice Returns the DISTRIBUTOR_ROLE identifier\n function DISTRIBUTOR_ROLE() external view returns (bytes32);\n\n /// @notice Returns the COLLECTION_ROLE identifier\n function COLLECTION_ROLE() external view returns (bytes32);\n\n /// @notice Returns the FEE_ADMIN_ROLE identifier\n function FEE_ADMIN_ROLE() external view returns (bytes32);\n\n /*//////////////////////////////////////////////////////////////\n ROLE MANAGEMENT\n //////////////////////////////////////////////////////////////*/\n\n /// @notice Grant a role to an account\n function grantRole(bytes32 role, address account) external;\n\n /// @notice Revoke a role from an account\n function revokeRole(bytes32 role, address account) external;\n\n /// @notice Check if an account has a role\n function hasRole(bytes32 role, address account) external view returns (bool);\n /**\n * @notice Called by the NFT contract during a transfer to atomically distribute royalties\n * @param tokenId The token being transferred\n * @param from The seller\n * @param salePrice The sale price\n */\n function onTransferWithValue(\n uint256 tokenId,\n address from,\n uint256 salePrice\n ) external payable;\n\n /**\n * @notice Register a pending sale for atomic distribution\n * @param tokenId The token about to be sold\n * @return saleId A unique identifier for this sale\n */\n function registerSale(uint256 tokenId) external returns (bytes32 saleId);\n\n /**\n * @notice Receive payment for a specific sale and distribute immediately\n * @param saleId The unique sale identifier from registerSale()\n */\n function receivePaymentForSale(bytes32 saleId) external payable;\n\n /**\n * @notice Receive payment with tokenId directly specified\n * @param tokenId The token that was sold\n */\n function receivePaymentForToken(uint256 tokenId) external payable;\n\n /**\n * @notice Register creator info for a token\n * @param tokenId The token ID\n * @param creator The creator's address\n * @param creatorCutBP The creator's royalty cut in basis points\n */\n function registerCreator(\n uint256 tokenId,\n address creator,\n uint96 creatorCutBP\n ) external;\n\n /**\n * @notice Batch register creators\n */\n function registerCreatorsBatch(\n uint256[] calldata tokenIds,\n address[] calldata creators,\n uint96[] calldata creatorCutsBP\n ) external;\n\n /**\n * @notice Get the split breakdown for a token\n */\n function getSplit(uint256 tokenId, uint256 amount) \n external \n view \n returns (\n uint256 manufacturerAmount,\n uint256 partnerAmount,\n uint256 creatorAmount,\n address creator,\n uint96 creatorCutBP\n );\n\n /**\n * @notice Get creator info for a token\n */\n function getCreatorInfo(uint256 tokenId)\n external\n view\n returns (address creator, uint96 cutBP);\n\n /**\n * @notice Check if a token has creator info registered\n */\n function hasCreatorInfo(uint256 tokenId) external view returns (bool);\n\n /**\n * @notice Get pending ETH for an address\n */\n function getPendingETH(address recipient) external view returns (uint256);\n\n /**\n * @notice Withdraw accumulated ETH\n */\n function withdrawETH() external;\n\n /**\n * @notice Withdraw accumulated ERC20 tokens\n */\n function withdrawERC20(address token) external;\n\n /*//////////////////////////////////////////////////////////////\n ADMIN FUNCTIONS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Enable or disable push (immediate) distribution mode\n */\n function setPushMode(bool enabled) external;\n\n /**\n * @notice Update wallet addresses\n */\n function setWallets(address manufacturerWallet, address partnerWallet) external;\n\n /**\n * @notice Update cut percentages\n */\n function setCuts(\n uint96 manufacturerCutBP,\n uint96 partnerCutBP,\n uint96 defaultCreatorCutBP\n ) external;\n\n /**\n * @notice Allows a registered creator to change their own receiving wallet\n * @param tokenId The token ID for which they are the creator\n * @param newWallet The new wallet address to receive royalties\n */\n function updateCreatorWallet(uint256 tokenId, address newWallet) external;\n\n /**\n * @notice Admin/Distributor can override creator registration\n * @param tokenId The token ID\n * @param creator The new creator address\n * @param creatorCutBP The creator's royalty cut in basis points\n */\n function overrideCreator(\n uint256 tokenId,\n address creator,\n uint96 creatorCutBP\n ) external;\n}\n"},"node_modules/@openzeppelin/contracts/proxy/beacon/IBeacon.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (proxy/beacon/IBeacon.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeacon {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {UpgradeableBeacon} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n"},"node_modules/@openzeppelin/contracts/access/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n /**\n * @dev The caller account is not authorized to perform an operation.\n */\n error OwnableUnauthorizedAccount(address account);\n\n /**\n * @dev The owner is not a valid owner account. (eg. `address(0)`)\n */\n error OwnableInvalidOwner(address owner);\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n */\n constructor(address initialOwner) {\n if (initialOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(initialOwner);\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n if (owner() != _msgSender()) {\n revert OwnableUnauthorizedAccount(_msgSender());\n }\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n if (newOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n"},"node_modules/@openzeppelin/contracts/proxy/Proxy.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\n * be specified by overriding the virtual {_implementation} function.\n *\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\n * different contract through the {_delegate} function.\n *\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\n */\nabstract contract Proxy {\n /**\n * @dev Delegates the current call to `implementation`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _delegate(address implementation) internal virtual {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback\n * function and {_fallback} should delegate.\n */\n function _implementation() internal view virtual returns (address);\n\n /**\n * @dev Delegates the current call to the address returned by `_implementation()`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _fallback() internal virtual {\n _delegate(_implementation());\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\n * function in the contract matches the call data.\n */\n fallback() external payable virtual {\n _fallback();\n }\n}\n"},"node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (proxy/ERC1967/ERC1967Utils.sol)\n\npragma solidity ^0.8.21;\n\nimport {IBeacon} from \"../beacon/IBeacon.sol\";\nimport {IERC1967} from \"../../interfaces/IERC1967.sol\";\nimport {Address} from \"../../utils/Address.sol\";\nimport {StorageSlot} from \"../../utils/StorageSlot.sol\";\n\n/**\n * @dev This library provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.\n */\nlibrary ERC1967Utils {\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1.\n */\n // solhint-disable-next-line private-vars-leading-underscore\n bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev The `implementation` of the proxy is invalid.\n */\n error ERC1967InvalidImplementation(address implementation);\n\n /**\n * @dev The `admin` of the proxy is invalid.\n */\n error ERC1967InvalidAdmin(address admin);\n\n /**\n * @dev The `beacon` of the proxy is invalid.\n */\n error ERC1967InvalidBeacon(address beacon);\n\n /**\n * @dev An upgrade function sees `msg.value > 0` that may be lost.\n */\n error ERC1967NonPayable();\n\n /**\n * @dev Returns the current implementation address.\n */\n function getImplementation() internal view returns (address) {\n return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the ERC-1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n if (newImplementation.code.length == 0) {\n revert ERC1967InvalidImplementation(newImplementation);\n }\n StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Performs implementation upgrade with additional setup call if data is nonempty.\n * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected\n * to avoid stuck value in the contract.\n *\n * Emits an {IERC1967-Upgraded} event.\n */\n function upgradeToAndCall(address newImplementation, bytes memory data) internal {\n _setImplementation(newImplementation);\n emit IERC1967.Upgraded(newImplementation);\n\n if (data.length > 0) {\n Address.functionDelegateCall(newImplementation, data);\n } else {\n _checkNonPayable();\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1.\n */\n // solhint-disable-next-line private-vars-leading-underscore\n bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Returns the current admin.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using\n * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\n */\n function getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the ERC-1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n if (newAdmin == address(0)) {\n revert ERC1967InvalidAdmin(address(0));\n }\n StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {IERC1967-AdminChanged} event.\n */\n function changeAdmin(address newAdmin) internal {\n emit IERC1967.AdminChanged(getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is the keccak-256 hash of \"eip1967.proxy.beacon\" subtracted by 1.\n */\n // solhint-disable-next-line private-vars-leading-underscore\n bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Returns the current beacon.\n */\n function getBeacon() internal view returns (address) {\n return StorageSlot.getAddressSlot(BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the ERC-1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n if (newBeacon.code.length == 0) {\n revert ERC1967InvalidBeacon(newBeacon);\n }\n\n StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;\n\n address beaconImplementation = IBeacon(newBeacon).implementation();\n if (beaconImplementation.code.length == 0) {\n revert ERC1967InvalidImplementation(beaconImplementation);\n }\n }\n\n /**\n * @dev Change the beacon and trigger a setup call if data is nonempty.\n * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected\n * to avoid stuck value in the contract.\n *\n * Emits an {IERC1967-BeaconUpgraded} event.\n *\n * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since\n * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for\n * efficiency.\n */\n function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {\n _setBeacon(newBeacon);\n emit IERC1967.BeaconUpgraded(newBeacon);\n\n if (data.length > 0) {\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\n } else {\n _checkNonPayable();\n }\n }\n\n /**\n * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract\n * if an upgrade doesn't perform an initialization call.\n */\n function _checkNonPayable() private {\n if (msg.value > 0) {\n revert ERC1967NonPayable();\n }\n }\n}\n"},"node_modules/@openzeppelin/contracts/access/IAccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (access/IAccessControl.sol)\n\npragma solidity >=0.8.4;\n\n/**\n * @dev External interface of AccessControl declared to support ERC-165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev The `account` is missing a role.\n */\n error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n /**\n * @dev The caller of a function is not the expected one.\n *\n * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n */\n error AccessControlBadConfirmation();\n\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted to signal this.\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).\n * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n */\n function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"},"node_modules/@openzeppelin/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n}\n"},"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n /// @inheritdoc IERC165\n function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n"},"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-20 standard as defined in the ERC.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the value of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the value of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 value) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n * caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 value) external returns (bool);\n\n /**\n * @dev Moves a `value` amount of tokens from `from` to `to` using the\n * allowance mechanism. `value` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC1363} from \"../../../interfaces/IERC1363.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC-20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n /**\n * @dev An operation with an ERC-20 token failed.\n */\n error SafeERC20FailedOperation(address token);\n\n /**\n * @dev Indicates a failed `decreaseAllowance` request.\n */\n error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n /**\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n }\n\n /**\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n */\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n }\n\n /**\n * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.\n */\n function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {\n return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));\n }\n\n /**\n * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.\n */\n function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {\n return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n }\n\n /**\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n *\n * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n */\n function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n uint256 oldAllowance = token.allowance(address(this), spender);\n forceApprove(token, spender, oldAllowance + value);\n }\n\n /**\n * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n * value, non-reverting calls are assumed to be successful.\n *\n * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n */\n function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n unchecked {\n uint256 currentAllowance = token.allowance(address(this), spender);\n if (currentAllowance < requestedDecrease) {\n revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n }\n forceApprove(token, spender, currentAllowance - requestedDecrease);\n }\n }\n\n /**\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n * to be set to zero before setting it to a non-zero value, such as USDT.\n *\n * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function\n * only sets the \"standard\" allowance. Any temporary allowance will remain active, in addition to the value being\n * set here.\n */\n function forceApprove(IERC20 token, address spender, uint256 value) internal {\n bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n if (!_callOptionalReturnBool(token, approvalCall)) {\n _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n _callOptionalReturn(token, approvalCall);\n }\n }\n\n /**\n * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no\n * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n * targeting contracts.\n *\n * Reverts if the returned value is other than `true`.\n */\n function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n if (to.code.length == 0) {\n safeTransfer(token, to, value);\n } else if (!token.transferAndCall(to, value, data)) {\n revert SafeERC20FailedOperation(address(token));\n }\n }\n\n /**\n * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target\n * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n * targeting contracts.\n *\n * Reverts if the returned value is other than `true`.\n */\n function transferFromAndCallRelaxed(\n IERC1363 token,\n address from,\n address to,\n uint256 value,\n bytes memory data\n ) internal {\n if (to.code.length == 0) {\n safeTransferFrom(token, from, to, value);\n } else if (!token.transferFromAndCall(from, to, value, data)) {\n revert SafeERC20FailedOperation(address(token));\n }\n }\n\n /**\n * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no\n * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n * targeting contracts.\n *\n * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.\n * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}\n * once without retrying, and relies on the returned value to be true.\n *\n * Reverts if the returned value is other than `true`.\n */\n function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n if (to.code.length == 0) {\n forceApprove(token, to, value);\n } else if (!token.approveAndCall(to, value, data)) {\n revert SafeERC20FailedOperation(address(token));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n uint256 returnSize;\n uint256 returnValue;\n assembly (\"memory-safe\") {\n let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n // bubble errors\n if iszero(success) {\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, returndatasize())\n revert(ptr, returndatasize())\n }\n returnSize := returndatasize()\n returnValue := mload(0)\n }\n\n if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {\n revert SafeERC20FailedOperation(address(token));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.\n */\n function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n bool success;\n uint256 returnSize;\n uint256 returnValue;\n assembly (\"memory-safe\") {\n success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n returnSize := returndatasize()\n returnValue := mload(0)\n }\n return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);\n }\n}\n"},"node_modules/@openzeppelin/contracts/utils/ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,\n * consider using {ReentrancyGuardTransient} instead.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant NOT_ENTERED = 1;\n uint256 private constant ENTERED = 2;\n\n uint256 private _status;\n\n /**\n * @dev Unauthorized reentrant call.\n */\n error ReentrancyGuardReentrantCall();\n\n constructor() {\n _status = NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be NOT_ENTERED\n if (_status == ENTERED) {\n revert ReentrancyGuardReentrantCall();\n }\n\n // Any calls to nonReentrant after this point will fail\n _status = ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = NOT_ENTERED;\n }\n\n /**\n * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n * `nonReentrant` function in the call stack.\n */\n function _reentrancyGuardEntered() internal view returns (bool) {\n return _status == ENTERED;\n }\n}\n"},"node_modules/@openzeppelin/contracts/interfaces/IERC1967.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1967.sol)\n\npragma solidity >=0.4.11;\n\n/**\n * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.\n */\ninterface IERC1967 {\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Emitted when the beacon is changed.\n */\n event BeaconUpgraded(address indexed beacon);\n}\n"},"node_modules/@openzeppelin/contracts/utils/Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\nimport {Errors} from \"./Errors.sol\";\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev There's no code at `target` (it is not a contract).\n */\n error AddressEmptyCode(address target);\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n if (address(this).balance < amount) {\n revert Errors.InsufficientBalance(address(this).balance, amount);\n }\n\n (bool success, bytes memory returndata) = recipient.call{value: amount}(\"\");\n if (!success) {\n _revert(returndata);\n }\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason or custom error, it is bubbled\n * up by this function (like regular Solidity function calls). However, if\n * the call reverted with no returned reason, this function reverts with a\n * {Errors.FailedCall} error.\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n if (address(this).balance < value) {\n revert Errors.InsufficientBalance(address(this).balance, value);\n }\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case\n * of an unsuccessful call.\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata\n ) internal view returns (bytes memory) {\n if (!success) {\n _revert(returndata);\n } else {\n // only check if target is a contract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n if (returndata.length == 0 && target.code.length == 0) {\n revert AddressEmptyCode(target);\n }\n return returndata;\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n * revert reason or with a default {Errors.FailedCall} error.\n */\n function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n if (!success) {\n _revert(returndata);\n } else {\n return returndata;\n }\n }\n\n /**\n * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.\n */\n function _revert(bytes memory returndata) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n assembly (\"memory-safe\") {\n revert(add(returndata, 0x20), mload(returndata))\n }\n } else {\n revert Errors.FailedCall();\n }\n }\n}\n"},"node_modules/@openzeppelin/contracts/utils/StorageSlot.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC-1967 implementation slot:\n * ```solidity\n * contract ERC1967 {\n * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(newImplementation.code.length > 0);\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * TIP: Consider using this library along with {SlotDerivation}.\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n struct Int256Slot {\n int256 value;\n }\n\n struct StringSlot {\n string value;\n }\n\n struct BytesSlot {\n bytes value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns a `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns a `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns a `Int256Slot` with member `value` located at `slot`.\n */\n function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns a `StringSlot` with member `value` located at `slot`.\n */\n function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\n */\n function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := store.slot\n }\n }\n\n /**\n * @dev Returns a `BytesSlot` with member `value` located at `slot`.\n */\n function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\n */\n function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\n assembly (\"memory-safe\") {\n r.slot := store.slot\n }\n }\n}\n"},"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[ERC].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},"node_modules/@openzeppelin/contracts/interfaces/IERC1363.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @title IERC1363\n * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].\n *\n * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract\n * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.\n */\ninterface IERC1363 is IERC20, IERC165 {\n /*\n * Note: the ERC-165 identifier for this interface is 0xb0202a11.\n * 0xb0202a11 ===\n * bytes4(keccak256('transferAndCall(address,uint256)')) ^\n * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^\n * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^\n * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^\n * bytes4(keccak256('approveAndCall(address,uint256)')) ^\n * bytes4(keccak256('approveAndCall(address,uint256,bytes)'))\n */\n\n /**\n * @dev Moves a `value` amount of tokens from the caller's account to `to`\n * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n * @param to The address which you want to transfer to.\n * @param value The amount of tokens to be transferred.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function transferAndCall(address to, uint256 value) external returns (bool);\n\n /**\n * @dev Moves a `value` amount of tokens from the caller's account to `to`\n * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n * @param to The address which you want to transfer to.\n * @param value The amount of tokens to be transferred.\n * @param data Additional data with no specified format, sent in call to `to`.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);\n\n /**\n * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n * @param from The address which you want to send tokens from.\n * @param to The address which you want to transfer to.\n * @param value The amount of tokens to be transferred.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function transferFromAndCall(address from, address to, uint256 value) external returns (bool);\n\n /**\n * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n * @param from The address which you want to send tokens from.\n * @param to The address which you want to transfer to.\n * @param value The amount of tokens to be transferred.\n * @param data Additional data with no specified format, sent in call to `to`.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);\n\n /**\n * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n * @param spender The address which will spend the funds.\n * @param value The amount of tokens to be spent.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function approveAndCall(address spender, uint256 value) external returns (bool);\n\n /**\n * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n * @param spender The address which will spend the funds.\n * @param value The amount of tokens to be spent.\n * @param data Additional data with no specified format, sent in call to `spender`.\n * @return A boolean value indicating whether the operation succeeded unless throwing.\n */\n function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);\n}\n"},"node_modules/@openzeppelin/contracts/utils/Errors.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of common custom errors used in multiple contracts\n *\n * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.\n * It is recommended to avoid relying on the error API for critical functionality.\n *\n * _Available since v5.1._\n */\nlibrary Errors {\n /**\n * @dev The ETH balance of the account is not enough to perform the operation.\n */\n error InsufficientBalance(uint256 balance, uint256 needed);\n\n /**\n * @dev A call to an address target failed. The target may have reverted.\n */\n error FailedCall();\n\n /**\n * @dev The deployment failed.\n */\n error FailedDeployment();\n\n /**\n * @dev A necessary precompile is missing.\n */\n error MissingPrecompile(address);\n}\n"},"node_modules/@openzeppelin/contracts/interfaces/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC20} from \"../token/ERC20/IERC20.sol\";\n"},"node_modules/@openzeppelin/contracts/interfaces/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC165} from \"../utils/introspection/IERC165.sol\";\n"}},"settings":{"remappings":["@openzeppelin/=node_modules/@openzeppelin/","@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","hardhat/=node_modules/hardhat/","@arbitrum/=node_modules/@chainlink/contracts/node_modules/@arbitrum/","@chainlink/=node_modules/@chainlink/","@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/","@limitbreak/permit-c/=lib/creator-token-standards/lib/PermitC/src/","@offchainlabs/=node_modules/@offchainlabs/","@opensea/tstorish/=lib/creator-token-standards/lib/tstorish/src/","@rari-capital/solmate/=lib/creator-token-standards/lib/PermitC/lib/solmate/","@scroll-tech/=node_modules/@scroll-tech/","@zksync/=node_modules/@zksync/","ERC721A/=lib/creator-token-standards/lib/ERC721A/contracts/","PermitC/=lib/creator-token-standards/lib/PermitC/","creator-token-standards/=lib/creator-token-standards/","ds-test/=lib/creator-token-standards/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/creator-token-standards/lib/PermitC/lib/openzeppelin-contracts/lib/erc4626-tests/","erc721a/=lib/creator-token-standards/lib/ERC721A/","forge-gas-metering/=lib/creator-token-standards/lib/PermitC/lib/forge-gas-metering/","forge-std/=lib/forge-std/src/","murky/=lib/creator-token-standards/lib/murky/","openzeppelin-contracts/=lib/creator-token-standards/lib/openzeppelin-contracts/","openzeppelin/=lib/creator-token-standards/lib/PermitC/lib/openzeppelin-contracts/contracts/","solady/=node_modules/solady/","solidity-bytes-utils/=node_modules/solidity-bytes-utils/","solmate/=lib/creator-token-standards/lib/PermitC/lib/solmate/src/","tstorish/=lib/creator-token-standards/lib/tstorish/src/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"prague","viaIR":true,"libraries":{}}}