From c87ff39c29f259196f3f6b7b5efdbeca6fcd82af Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Fri, 11 Apr 2025 18:53:43 +0900 Subject: [PATCH 01/26] Added auto generated python classes for group module --- .../GroupingModule/GroupingModule_client.py | 13 +++++++++ .../GroupingWorkflows_client.py | 6 ++++ .../scripts/config.json | 28 +++++++++---------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py b/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py index af94956..2dab14e 100644 --- a/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py +++ b/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py @@ -21,4 +21,17 @@ def __init__(self, web3: Web3): with open(abi_path, 'r') as abi_file: abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) + + def addIp(self, groupIpId, ipIds): + return self.contract.functions.addIp(groupIpId, ipIds).transact() + + def build_addIp_transaction(self, groupIpId, ipIds, tx_params): + return self.contract.functions.addIp(groupIpId, ipIds).build_transaction(tx_params) + + def registerGroup(self, groupPool): + return self.contract.functions.registerGroup(groupPool).transact() + + def build_registerGroup_transaction(self, groupPool, tx_params): + return self.contract.functions.registerGroup(groupPool).build_transaction(tx_params) + \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py b/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py index c31d3ff..6f6c725 100644 --- a/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py +++ b/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py @@ -22,6 +22,12 @@ def __init__(self, web3: Web3): abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) + def collectRoyaltiesAndClaimReward(self, groupIpId, currencyTokens, memberIpIds): + return self.contract.functions.collectRoyaltiesAndClaimReward(groupIpId, currencyTokens, memberIpIds).transact() + + def build_collectRoyaltiesAndClaimReward_transaction(self, groupIpId, currencyTokens, memberIpIds, tx_params): + return self.contract.functions.collectRoyaltiesAndClaimReward(groupIpId, currencyTokens, memberIpIds).build_transaction(tx_params) + def mintAndRegisterIpAndAttachLicenseAndAddToGroup(self, spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates): return self.contract.functions.mintAndRegisterIpAndAttachLicenseAndAddToGroup(spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates).transact() diff --git a/src/story_protocol_python_sdk/scripts/config.json b/src/story_protocol_python_sdk/scripts/config.json index cfb0e4d..1286088 100644 --- a/src/story_protocol_python_sdk/scripts/config.json +++ b/src/story_protocol_python_sdk/scripts/config.json @@ -126,19 +126,10 @@ "mintAndRegisterIpAndAttachLicenseAndAddToGroup", "registerIpAndAttachLicenseAndAddToGroup", "registerGroupAndAttachLicense", - "registerGroupAndAttachLicenseAndAddIps" - ] - }, - { - "contract_name": "DerivativeWorkflows", - "contract_address": "0x9e2d496f72C547C2C535B167e06ED8729B374a4f", - "functions": [ - "mintAndRegisterIpAndMakeDerivativeWithLicenseTokens", - "registerIpAndMakeDerivative", - "mintAndRegisterIpAndMakeDerivative", - "registerIpAndMakeDerivativeWithLicenseTokens", - "mintAndRegisterIpAndMakeDerivativeWithLicenseTokens", - "multicall" + "registerGroupAndAttachLicenseAndAddIps", + "collectRoyaltiesAndClaimReward", + "CollectedRoyaltiesToGroupPool", + "RoyaltyPaid" ] }, { @@ -197,7 +188,11 @@ { "contract_name": "GroupingModule", "contract_address": "0x69D3a7aa9edb72Bc226E745A7cCdd50D947b69Ac", - "functions": [] + "functions": [ + "registerGroup", + "addIp", + "IPGroupRegistered" + ] }, { "contract_name": "LicenseRegistry", @@ -249,6 +244,11 @@ "mintFeeToken", "mintFee" ] + }, + { + "contract_name": "DerivativeWorkflows", + "contract_address": "0x9e2d496f72C547C2C535B167e06ED8729B374a4f", + "functions": [] } ] } From 0359b7a4f81f735a6e8ab4d5b4610ac1bd044fae Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Fri, 11 Apr 2025 18:54:31 +0900 Subject: [PATCH 02/26] Added EVEN_SPLIT_GROUP_POOL constant to utils for integration test --- tests/integration/setup_for_integration.py | 6 ++++-- tests/integration/utils.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/setup_for_integration.py b/tests/integration/setup_for_integration.py index cc075c3..63bced2 100644 --- a/tests/integration/setup_for_integration.py +++ b/tests/integration/setup_for_integration.py @@ -27,7 +27,8 @@ ARBITRATION_POLICY_UMA, generate_cid, WIP_TOKEN_ADDRESS, - setup_royalty_vault + setup_royalty_vault, + EVEN_SPLIT_GROUP_POOL ) # Load environment variables @@ -86,5 +87,6 @@ def story_client_2(): 'wallet_address', 'wallet_address_2', 'private_key', - 'private_key_2' + 'private_key_2', + 'EVEN_SPLIT_GROUP_POOL' ] \ No newline at end of file diff --git a/tests/integration/utils.py b/tests/integration/utils.py index ea205a1..29498bc 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -19,6 +19,7 @@ ROYALTY_MODULE="0xD2f60c40fEbccf6311f8B47c4f2Ec6b040400086" PIL_LICENSE_TEMPLATE="0x2E896b0b2Fdb7457499B56AAaA4AE55BCB4Cd316" ARBITRATION_POLICY_UMA="0xfFD98c3877B8789124f02C7E8239A4b0Ef11E936" +EVEN_SPLIT_GROUP_POOL="0xf96f2c30b41Cb6e0290de43C8528ae83d4f33F89" def get_story_client_in_sepolia(web3: Web3, account) -> StoryClient: chain_id = 11155111 # Sepolia chain ID From 50fc9efdad99ca31af5afe55cb09c4e699f5767f Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Fri, 11 Apr 2025 18:54:53 +0900 Subject: [PATCH 03/26] Added group module to story client --- src/story_protocol_python_sdk/story_client.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/story_protocol_python_sdk/story_client.py b/src/story_protocol_python_sdk/story_client.py index e66adf9..88015fa 100644 --- a/src/story_protocol_python_sdk/story_client.py +++ b/src/story_protocol_python_sdk/story_client.py @@ -17,6 +17,7 @@ from story_protocol_python_sdk.resources.NFTClient import NFTClient from story_protocol_python_sdk.resources.Dispute import Dispute from story_protocol_python_sdk.resources.WIP import WIP +from story_protocol_python_sdk.resources.Group import Group class StoryClient: """ @@ -53,6 +54,7 @@ def __init__(self, web3, account, chain_id: int): self._nft_client = None self._dispute = None self._wip = None + self._group = None @property def IPAsset(self) -> IPAsset: @@ -142,6 +144,15 @@ def WIP(self) -> WIP: self._wip = WIP(self.web3, self.account, self.chain_id) return self._wip + @property + def Group(self) -> Group: + """ + Access the Group resource. + """ + if self._group is None: + self._group = Group(self.web3, self.account, self.chain_id) + return self._group + def get_wallet_balance(self) -> int: """ Get the WIP token balance of the current wallet. From d694606eeded4c5a01cd97b2a74829ca7bc508f4 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Fri, 11 Apr 2025 19:03:19 +0900 Subject: [PATCH 04/26] Added register_group() to group module --- .../resources/Group.py | 652 ++++++++++++++++++ tests/integration/test_integration_group.py | 453 ++++++++++++ 2 files changed, 1105 insertions(+) create mode 100644 src/story_protocol_python_sdk/resources/Group.py create mode 100644 tests/integration/test_integration_group.py diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py new file mode 100644 index 0000000..106bd1a --- /dev/null +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -0,0 +1,652 @@ +# src/story_protocol_python_sdk/resources/Group.py + +from web3 import Web3 + +from story_protocol_python_sdk.abi.GroupingModule.GroupingModule_client import GroupingModuleClient +from story_protocol_python_sdk.abi.GroupingWorkflows.GroupingWorkflows_client import GroupingWorkflowsClient +from story_protocol_python_sdk.abi.IPAssetRegistry.IPAssetRegistry_client import IPAssetRegistryClient +from story_protocol_python_sdk.abi.CoreMetadataModule.CoreMetadataModule_client import CoreMetadataModuleClient +from story_protocol_python_sdk.abi.LicensingModule.LicensingModule_client import LicensingModuleClient +from story_protocol_python_sdk.abi.LicenseRegistry.LicenseRegistry_client import LicenseRegistryClient +from story_protocol_python_sdk.abi.PILicenseTemplate.PILicenseTemplate_client import PILicenseTemplateClient +from story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client import IPAccountImplClient + +from story_protocol_python_sdk.utils.license_terms import LicenseTerms +from story_protocol_python_sdk.utils.transaction_utils import build_and_send_transaction +from story_protocol_python_sdk.utils.sign import Sign +from story_protocol_python_sdk.utils.constants import ZERO_ADDRESS, ZERO_HASH + + +class Group: + """ + A class to manage groups on Story Protocol. + + :param web3 Web3: An instance of Web3. + :param account: The account to use for transactions. + :param chain_id int: The ID of the blockchain network. + """ + def __init__(self, web3: Web3, account, chain_id: int): + self.web3 = web3 + self.account = account + self.chain_id = chain_id + + self.grouping_module_client = GroupingModuleClient(web3) + self.grouping_workflows_client = GroupingWorkflowsClient(web3) + self.ip_asset_registry_client = IPAssetRegistryClient(web3) + self.core_metadata_module_client = CoreMetadataModuleClient(web3) + self.licensing_module_client = LicensingModuleClient(web3) + self.license_registry_client = LicenseRegistryClient(web3) + self.pi_license_template_client = PILicenseTemplateClient(web3) + + self.license_terms_util = LicenseTerms(web3) + self.sign = Sign(web3, account, chain_id) + + def register_group(self, group_pool: str, tx_options: dict = None) -> dict: + """ + Registers a Group IPA. + + :param group_pool str: The address of the group pool. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash and group ID. + """ + try: + if not Web3.is_address(group_pool): + raise ValueError(f'Address "{group_pool}" is invalid.') + + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_module_client.build_registerGroup_transaction, + group_pool, + tx_options=tx_options + ) + + group_id = self._parse_tx_ip_group_registered_event(response['tx_receipt']) + + return { + 'tx_hash': response['tx_hash'], + 'group_id': group_id + } + + except Exception as e: + raise ValueError(f"Failed to register group: {str(e)}") + + + def register_group_and_attach_license( + self, + group_pool: str, + license_data: dict, + tx_options: dict = None + ) -> dict: + """ + Register a group IP with a group reward pool and attach license terms to the group IP. + + :param group_pool str: The address of the group pool. + :param license_data dict: License data object with terms and config. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash and group ID. + """ + try: + if not self.web3.is_address(group_pool): + raise ValueError(f'Group pool address "{group_pool}" is invalid.') + + # Process license data + license_data_processed = self._get_license_data([license_data])[0] + + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_workflows_client.build_registerGroupAndAttachLicense_transaction, + group_pool, + license_data_processed, + tx_options=tx_options + ) + + group_id = self._parse_tx_ip_group_registered_event(response['tx_receipt']) + + return { + 'tx_hash': response['tx_hash'], + 'group_id': group_id + } + + except Exception as e: + raise ValueError(f"Failed to register group and attach license: {str(e)}") + + # def mint_and_register_ip_and_attach_license_and_add_to_group( + # self, + # group_id: str, + # spg_nft_contract: str, + # license_data: list, + # max_allowed_reward_share: int, + # ip_metadata: dict = None, + # recipient: str = None, + # allow_duplicates: bool = True, + # deadline: int = None, + # tx_options: dict = None + # ) -> dict: + # """ + # Mint an NFT from a SPGNFT collection, register it with metadata as an IP, + # attach license terms to the registered IP, and add it to a group IP. + + # :param group_id str: The ID of the group to add the IP to. + # :param spg_nft_contract str: The address of the SPG NFT contract. + # :param license_data list: List of license data objects with terms and config. + # :param max_allowed_reward_share int: Maximum allowed reward share percentage. + # :param ip_metadata dict: [Optional] The metadata for the IP. + # :param recipient str: [Optional] The recipient of the NFT (defaults to caller). + # :param allow_duplicates bool: [Optional] Whether to allow duplicate IPs. + # :param deadline int: [Optional] The deadline for the signature in milliseconds. + # :param tx_options dict: [Optional] The transaction options. + # :return dict: A dictionary with the transaction hash, IP ID, and token ID. + # """ + # try: + # if not Web3.is_address(group_id): + # raise ValueError(f'Group ID "{group_id}" is invalid.') + + # if not Web3.is_address(spg_nft_contract): + # raise ValueError(f'SPG NFT contract address "{spg_nft_contract}" is invalid.') + + # is_registered = self.ip_asset_registry_client.isRegistered(group_id) + # if not is_registered: + # raise ValueError(f"Group IP {group_id} is not registered.") + + # # Get IP account state + # ip_account = IPAccountImplClient(self.web3, group_id) + # state = ip_account.state() + + # # Calculate deadline + # calculated_deadline = self.sign._get_deadline(deadline=deadline) + + # # Get permission signature for adding to group + # sig_add_to_group = self.sign.get_permission_signature( + # ip_id=group_id, + # deadline=calculated_deadline, + # state=state, + # permissions=[{ + # 'signer': self.grouping_workflows_client.contract.address, + # 'to': self.grouping_module_client.contract.address, + # 'permission': 1, # ALLOW + # 'func': "addIp(address,address,uint256)" + # }] + # ) + + # # Process license data + # licenses_data = self._get_license_data(license_data) + + # # Process IP metadata + # metadata = self._get_ip_metadata(ip_metadata) + + # # Set recipient to caller if not provided + # if not recipient: + # recipient = self.account.address + + # response = build_and_send_transaction( + # self.web3, + # self.account, + # self.grouping_workflows_client.build_mintAndRegisterIpAndAttachLicenseAndAddToGroup_transaction, + # spg_nft_contract, + # recipient, + # metadata, + # licenses_data, + # group_id, + # self.license_terms_util.get_revenue_share(max_allowed_reward_share), + # allow_duplicates, + # { + # 'signer': self.account.address, + # 'deadline': calculated_deadline, + # 'signature': sig_add_to_group + # }, + # tx_options=tx_options + # ) + + # # Parse events to get IP ID and token ID + # registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) + + # return { + # 'tx_hash': response['tx_hash'], + # 'ip_id': registration_data['ip_id'], + # 'token_id': registration_data['token_id'] + # } + + # except Exception as e: + # raise ValueError(f"Failed to mint and register IP and attach license and add to group: {str(e)}") + + # def register_ip_and_attach_license_and_add_to_group( + # self, + # group_id: str, + # nft_contract: str, + # token_id: int, + # license_data: list, + # max_allowed_reward_share: int, + # ip_metadata: dict = None, + # deadline: int = None, + # tx_options: dict = None + # ) -> dict: + # """ + # Register an NFT as IP with metadata, attach license terms to the registered IP, + # and add it to a group IP. + + # :param group_id str: The ID of the group to add the IP to. + # :param nft_contract str: The address of the NFT contract. + # :param token_id int: The token ID of the NFT. + # :param license_data list: List of license data objects with terms and config. + # :param max_allowed_reward_share int: Maximum allowed reward share percentage. + # :param ip_metadata dict: [Optional] The metadata for the IP. + # :param deadline int: [Optional] The deadline for the signature in milliseconds. + # :param tx_options dict: [Optional] The transaction options. + # :return dict: A dictionary with the transaction hash, IP ID, and token ID. + # """ + # try: + # if not Web3.is_address(group_id): + # raise ValueError(f'Group ID "{group_id}" is invalid.') + + # if not Web3.is_address(nft_contract): + # raise ValueError(f'NFT contract address "{nft_contract}" is invalid.') + + # # Check if group is registered + # is_registered = self.ip_asset_registry_client.isRegistered(group_id) + # if not is_registered: + # raise ValueError(f"Group IP {group_id} is not registered.") + + # # Get IP ID for the NFT + # ip_id = self.ip_asset_registry_client.ipId( + # self.chain_id, + # nft_contract, + # token_id + # ) + + # # Get IP account state + # ip_account = IPAccountImplClient(self.web3, group_id) + # state = ip_account.state() + + # # Calculate deadline + # calculated_deadline = self.sign._get_deadline(deadline=deadline) + + # # Get permission signature for adding to group + # sig_add_to_group = self.sign.get_permission_signature( + # ip_id=group_id, + # deadline=calculated_deadline, + # state=state, + # permissions=[{ + # 'signer': self.grouping_workflows_client.contract.address, + # 'to': self.grouping_module_client.contract.address, + # 'permission': 1, # ALLOW + # 'func': "addIp(address,address,uint256)" + # }] + # ) + + # # Get permission signature for metadata and license + # sig_metadata_and_attach = self.sign.get_permission_signature( + # ip_id=ip_id, + # deadline=calculated_deadline, + # state="0x0000000000000000000000000000000000000000000000000000000000000000", + # permissions=[ + # { + # 'signer': self.grouping_workflows_client.contract.address, + # 'to': self.core_metadata_module_client.contract.address, + # 'permission': 1, # ALLOW + # 'func': "setAll(address,string,bytes32,bytes32)" + # }, + # { + # 'signer': self.grouping_workflows_client.contract.address, + # 'to': self.licensing_module_client.contract.address, + # 'permission': 1, # ALLOW + # 'func': "attachLicenseTerms(address,address,uint256)" + # }, + # { + # 'signer': self.grouping_workflows_client.contract.address, + # 'to': self.licensing_module_client.contract.address, + # 'permission': 1, # ALLOW + # 'func': "setLicensingConfig(address,uint256,tuple)" + # } + # ] + # ) + + # # Process license data + # licenses_data = self._get_license_data(license_data) + + # # Process IP metadata + # metadata = self._get_ip_metadata(ip_metadata) + + # response = build_and_send_transaction( + # self.web3, + # self.account, + # self.grouping_workflows_client.build_registerIpAndAttachLicenseAndAddToGroup_transaction, + # nft_contract, + # token_id, + # group_id, + # metadata, + # licenses_data, + # self.license_terms_util.get_revenue_share(max_allowed_reward_share), + # { + # 'signer': self.account.address, + # 'deadline': calculated_deadline, + # 'signature': sig_add_to_group + # }, + # { + # 'signer': self.account.address, + # 'deadline': calculated_deadline, + # 'signature': sig_metadata_and_attach + # }, + # tx_options=tx_options + # ) + + # # Parse events to get IP ID and token ID + # registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) + + # return { + # 'tx_hash': response['tx_hash'], + # 'ip_id': registration_data['ip_id'], + # 'token_id': registration_data['token_id'] + # } + + # except Exception as e: + # raise ValueError(f"Failed to register IP and attach license and add to group: {str(e)}") + + # def register_group_and_attach_license_and_add_ips( + # self, + # group_pool: str, + # ip_ids: list, + # license_data: dict, + # max_allowed_reward_share: int, + # tx_options: dict = None + # ) -> dict: + # """ + # Register a group IP with a group reward pool, attach license terms to the group IP, + # and add individual IPs to the group IP. + + # :param group_pool str: The address of the group pool. + # :param ip_ids list: List of IP IDs to add to the group. + # :param license_data dict: License data object with terms and config. + # :param max_allowed_reward_share int: Maximum allowed reward share percentage. + # :param tx_options dict: [Optional] The transaction options. + # :return dict: A dictionary with the transaction hash and group ID. + # """ + # try: + # if not Web3.is_address(group_pool): + # raise ValueError(f'Group pool address "{group_pool}" is invalid.') + + # # Validate IP IDs + # for ip_id in ip_ids: + # if not Web3.is_address(ip_id): + # raise ValueError(f'IP ID "{ip_id}" is invalid.') + + # is_registered = self.ip_asset_registry_client.isRegistered(ip_id) + # if not is_registered: + # raise ValueError(f"IP {ip_id} is not registered.") + + # # Process license data + # license_data_processed = self._get_license_data([license_data])[0] + + # # Check if license terms are attached to all IPs + # for ip_id in ip_ids: + # is_attached = self.license_registry_client.hasIpAttachedLicenseTerms( + # ip_id, + # license_data_processed['licenseTemplate'], + # license_data_processed['licenseTermsId'] + # ) + # if not is_attached: + # raise ValueError( + # f"License terms must be attached to IP {ip_id} before adding to group." + # ) + + # response = build_and_send_transaction( + # self.web3, + # self.account, + # self.grouping_workflows_client.build_registerGroupAndAttachLicenseAndAddIps_transaction, + # group_pool, + # ip_ids, + # license_data_processed, + # self.license_terms_util.get_revenue_share(max_allowed_reward_share), + # tx_options=tx_options + # ) + + # group_id = self._parse_tx_ip_group_registered_event(response['tx_receipt']) + + # return { + # 'tx_hash': response['tx_hash'], + # 'group_id': group_id + # } + + # except Exception as e: + # raise ValueError(f"Failed to register group and attach license and add IPs: {str(e)}") + + # def collect_and_distribute_group_royalties( + # self, + # group_ip_id: str, + # currency_tokens: list, + # member_ip_ids: list, + # tx_options: dict = None + # ) -> dict: + # """ + # Collect royalties for the entire group and distribute the rewards to each member IP's royalty vault. + + # :param group_ip_id str: The ID of the group IP. + # :param currency_tokens list: List of currency token addresses. + # :param member_ip_ids list: List of member IP IDs. + # :param tx_options dict: [Optional] The transaction options. + # :return dict: A dictionary with the transaction hash and collected royalties. + # """ + # try: + # if not Web3.is_address(group_ip_id): + # raise ValueError(f'Group IP ID "{group_ip_id}" is invalid.') + + # if not currency_tokens: + # raise ValueError("At least one currency token is required.") + + # if not member_ip_ids: + # raise ValueError("At least one member IP ID is required.") + + # # Validate currency tokens + # for token in currency_tokens: + # if not Web3.is_address(token): + # raise ValueError(f'Currency token address "{token}" is invalid.') + + # if token == ZERO_ADDRESS: + # raise ValueError("Currency token cannot be the zero address.") + + # # Validate group IP + # is_group_registered = self.ip_asset_registry_client.isRegistered(group_ip_id) + # if not is_group_registered: + # raise ValueError(f"The group IP with ID {group_ip_id} is not registered.") + + # # Validate member IPs + # for ip_id in member_ip_ids: + # if not Web3.is_address(ip_id): + # raise ValueError(f'Member IP ID "{ip_id}" is invalid.') + + # is_member_registered = self.ip_asset_registry_client.isRegistered(ip_id) + # if not is_member_registered: + # raise ValueError(f"Member IP with ID {ip_id} is not registered.") + + # response = build_and_send_transaction( + # self.web3, + # self.account, + # self.grouping_workflows_client.build_collectRoyaltiesAndClaimReward_transaction, + # group_ip_id, + # currency_tokens, + # member_ip_ids, + # tx_options=tx_options + # ) + + # # Parse events to get collected royalties + # collected_royalties = self._parse_tx_collected_royalties_to_group_pool_event(response['tx_receipt']) + # royalties_distributed = self._parse_tx_royalty_paid_event(response['tx_receipt']) + + # return { + # 'tx_hash': response['tx_hash'], + # 'collected_royalties': collected_royalties, + # 'royalties_distributed': royalties_distributed + # } + + # except Exception as e: + # raise ValueError(f"Failed to collect and distribute group royalties: {str(e)}") + + def _get_license_data(self, license_data: list) -> list: + """ + Process license data into the format expected by the contracts. + + :param license_data list: List of license data objects. + :return list: Processed license data. + """ + if not license_data: + raise ValueError("License data is required.") + + result = [] + for item in license_data: + # Check if license_template is provided + if 'license_template' in item: + license_template = item['license_template'] + else: + license_template = self.pi_license_template_client.contract.address + + if not Web3.is_address(license_template): + raise ValueError(f'License template address "{license_template}" is invalid.') + + # Validate licensing config + licensing_config = item.get('licensing_config', {}) + + try: + self.license_terms_util.validate_licensing_config(licensing_config) + except Exception as e: + raise ValueError(f"Licensing config validation failed: {str(e)}") + + print(f"Original licensing_config: {licensing_config}") + + # Convert to camelCase for contract interaction + camelcase_config = { + 'isSet': licensing_config.get('is_set', True), + 'mintingFee': licensing_config.get('minting_fee', 0), + 'hookData': licensing_config.get('hook_data', ZERO_ADDRESS), + 'licensingHook': licensing_config.get('licensing_hook', ZERO_ADDRESS), + 'commercialRevShare': licensing_config.get('commercial_rev_share', 0), + 'disabled': licensing_config.get('disabled', False), + 'expectMinimumGroupRewardShare': licensing_config.get('expect_minimum_group_reward_share', 0), + 'expectGroupRewardPool': licensing_config.get('expect_group_reward_pool', ZERO_ADDRESS) + } + + processed_item = { + 'licenseTemplate': license_template, + 'licenseTermsId': item['license_terms_id'], + 'licensingConfig': camelcase_config + } + + result.append(processed_item) + + print(f"\n=== DEBUG: Final result from _get_license_data: {result} ===") + + return result + + def _get_ip_metadata(self, ip_metadata: dict = None) -> dict: + """ + Process IP metadata into the format expected by the contracts. + + :param ip_metadata dict: [Optional] The metadata for the IP. + :return dict: Processed IP metadata. + """ + metadata = { + 'ipMetadataURI': "", + 'ipMetadataHash': ZERO_HASH, + 'nftMetadataURI': "", + 'nftMetadataHash': ZERO_HASH, + } + + if ip_metadata: + metadata.update({ + 'ipMetadataURI': ip_metadata.get('ip_metadata_uri', ""), + 'ipMetadataHash': ip_metadata.get('ip_metadata_hash', ZERO_HASH), + 'nftMetadataURI': ip_metadata.get('nft_metadata_uri', ""), + 'nftMetadataHash': ip_metadata.get('nft_metadata_hash', ZERO_HASH), + }) + + return metadata + + def _parse_tx_ip_group_registered_event(self, tx_receipt: dict) -> str: + """ + Parse the IPGroupRegistered event from a transaction receipt. + + :param tx_receipt dict: The transaction receipt. + :return str: The group ID. + """ + event_signature = self.web3.keccak(text="IPGroupRegistered(address,address)").hex() + + for log in tx_receipt['logs']: + if log['topics'][0].hex() == event_signature: + group_id = '0x' + log['topics'][1].hex()[24:] + return self.web3.to_checksum_address(group_id) + + return None + + def _parse_tx_ip_registered_event(self, tx_receipt: dict) -> dict: + """ + Parse the IPRegistered event from a transaction receipt. + + :param tx_receipt dict: The transaction receipt. + :return dict: The IP ID and token ID. + """ + event_signature = self.web3.keccak( + text="IPRegistered(address,uint256,address,uint256,string,string,uint256)" + ).hex() + + for log in tx_receipt['logs']: + if log['topics'][0].hex() == event_signature: + ip_id = '0x' + log['data'].hex()[24:64] + token_id = int(log['topics'][3].hex(), 16) + + return { + 'ip_id': self.web3.to_checksum_address(ip_id), + 'token_id': token_id + } + + return None + + def _parse_tx_collected_royalties_to_group_pool_event(self, tx_receipt: dict) -> list: + """ + Parse the CollectedRoyaltiesToGroupPool event from a transaction receipt. + + :param tx_receipt dict: The transaction receipt. + :return list: List of collected royalties. + """ + event_signature = self.web3.keccak(text="CollectedRoyaltiesToGroupPool(address,uint256,address)").hex() + collected_royalties = [] + + for log in tx_receipt['logs']: + if log['topics'][0].hex() == event_signature: + group_id = '0x' + log['topics'][1].hex()[24:] + amount = int(log['data'][:66], 16) + token = '0x' + log['data'][90:130] + + collected_royalties.append({ + 'group_id': self.web3.to_checksum_address(group_id), + 'amount': amount, + 'token': self.web3.to_checksum_address(token) + }) + + return collected_royalties + + def _parse_tx_royalty_paid_event(self, tx_receipt: dict) -> list: + """ + Parse the RoyaltyPaid event from a transaction receipt. + + :param tx_receipt dict: The transaction receipt. + :return list: List of royalties distributed. + """ + event_signature = self.web3.keccak(text="RoyaltyPaid(address,uint256,address,uint256)").hex() + royalties_distributed = [] + + for log in tx_receipt['logs']: + if log['topics'][0].hex() == event_signature: + receiver_ip_id = '0x' + log['topics'][1].hex()[24:] + data = log['data'] + amount = int(data[:66], 16) + token = '0x' + data[90:130] + amount_after_fee = int(data[154:], 16) + + royalties_distributed.append({ + 'ip_id': self.web3.to_checksum_address(receiver_ip_id), + 'amount': amount, + 'token': self.web3.to_checksum_address(token), + 'amount_after_fee': amount_after_fee + }) + + return royalties_distributed diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py new file mode 100644 index 0000000..0e30163 --- /dev/null +++ b/tests/integration/test_integration_group.py @@ -0,0 +1,453 @@ +# tests/integration/test_integration_group.py + +import pytest +from web3 import Web3 + +from setup_for_integration import ( + web3, + account, + story_client, + get_token_id, + MockERC721, + MockERC20, + ZERO_ADDRESS, + ROYALTY_POLICY, + PIL_LICENSE_TEMPLATE, + EVEN_SPLIT_GROUP_POOL +) + +# class TestGroupBasicOperations: +# def test_register_basic_group(self, story_client): +# response = story_client.Group.register_group( +# group_pool=EVEN_SPLIT_GROUP_POOL +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'group_id' in response +# assert isinstance(response['group_id'], str) +# assert response['group_id'].startswith("0x") + +class TestGroupWithLicenseOperations: + @pytest.fixture(scope="module") + def nft_collection(self, story_client): + tx_data = story_client.NFTClient.create_nft_collection( + name="test-collection", + symbol="TEST", + max_supply=100, + is_public_minting=True, + mint_open=True, + contract_uri="test-uri", + mint_fee_recipient=account.address, + ) + return tx_data['nft_contract'] + + @pytest.fixture(scope="module") + def ip_with_license(self, story_client, nft_collection): + # Create initial IP with license terms + response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=[{ + 'terms': { + 'transferable': True, + 'royalty_policy': ROYALTY_POLICY, + 'default_minting_fee': 0, + 'expiration': 1000, + 'commercial_use': True, + 'commercial_attribution': False, + 'commercializer_checker': ZERO_ADDRESS, + 'commercializer_checker_data': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'commercial_rev_ceiling': 0, + 'derivatives_allowed': True, + 'derivatives_attribution': True, + 'derivatives_approval': False, + 'derivatives_reciprocal': True, + 'derivative_rev_ceiling': 0, + 'currency': MockERC20, + 'uri': "test case" + }, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }] + ) + + ip_id = response['ip_id'] + license_terms_id = response['license_terms_ids'][0] + + licensing_config = { + 'isSet': True, + 'mintingFee': 0, + 'licensingHook': ZERO_ADDRESS, + 'hookData': ZERO_ADDRESS, + 'commercialRevShare': 0, + 'disabled': False, + 'expectMinimumGroupRewardShare': 0, + 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL + } + + # Set licensing config + story_client.License.set_licensing_config( + ip_id=ip_id, + license_terms_id=license_terms_id, + license_template=PIL_LICENSE_TEMPLATE, + licensing_config=licensing_config + ) + + return { + 'ip_id': ip_id, + 'license_terms_id': license_terms_id + } + + @pytest.fixture(scope="module") + def group_with_license(self, story_client, ip_with_license): + response = story_client.Group.register_group_and_attach_license( + group_pool=EVEN_SPLIT_GROUP_POOL, + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) + + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + + assert response is not None + assert 'group_id' in response + assert response['group_id'] is not None + return response['group_id'] + + def test_register_group_and_attach_license(self, group_with_license): + assert group_with_license is not None + + # def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license): + # response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( + # group_id=group_with_license, + # spg_nft_contract=MockERC721, + # license_data=[{ + # 'license_terms_id': ip_with_license['license_terms_id'], + # 'licensing_config': { + # 'is_set': True, + # 'minting_fee': 0, + # 'hook_data': ZERO_ADDRESS, + # 'licensing_hook': ZERO_ADDRESS, + # 'commercial_rev_share': 0, + # 'disabled': False, + # 'expect_minimum_group_reward_share': 0, + # 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + # } + # }], + # max_allowed_reward_share=5 + # ) + + # assert 'tx_hash' in response + # assert isinstance(response['tx_hash'], str) + # assert len(response['tx_hash']) > 0 + + # assert 'ip_id' in response + # assert isinstance(response['ip_id'], str) + # assert response['ip_id'].startswith("0x") + +# class TestAdvancedGroupOperations: +# @pytest.fixture(scope="module") +# def ip_with_license(self, story_client): +# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=MockERC721, +# terms=[{ +# 'terms': { +# 'transferable': True, +# 'royalty_policy': ROYALTY_POLICY, +# 'default_minting_fee': 0, +# 'expiration': 1000, +# 'commercial_use': True, +# 'commercial_attribution': False, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'commercial_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_attribution': True, +# 'derivatives_approval': False, +# 'derivatives_reciprocal': True, +# 'derivative_rev_ceiling': 0, +# 'currency': MockERC20, +# 'uri': "test case" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) + +# return { +# 'ip_id': response['ip_id'], +# 'license_terms_id': response['license_terms_ids'][0] +# } + +# @pytest.fixture(scope="module") +# def group_id(self, story_client): +# response = story_client.Group.register_group( +# group_pool=EVEN_SPLIT_GROUP_POOL +# ) +# return response['group_id'] + +# def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): +# token_id = get_token_id(MockERC721, story_client.web3, story_client.account) + +# response = story_client.Group.register_ip_and_attach_license_and_add_to_group( +# group_id=group_id, +# nft_contract=MockERC721, +# token_id=token_id, +# max_allowed_reward_share=5, +# license_data=[{ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': "ZERO_ADDRESS", +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'ip_id' in response +# assert isinstance(response['ip_id'], str) +# assert response['ip_id'].startswith("0x") + +# def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): +# response = story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=5, +# ip_ids=[ip_with_license['ip_id']], +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'group_id' in response +# assert isinstance(response['group_id'], str) +# assert response['group_id'].startswith("0x") + +# def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): +# with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): +# story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=5, +# ip_ids=[ZERO_ADDRESS], # Invalid IP address +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) + +# class TestCollectRoyaltyAndClaimReward: +# @pytest.fixture(scope="module") +# def setup_royalty_collection(self, story_client): +# # Create license terms data +# license_terms_data = [{ +# 'terms': { +# 'commercial_attribution': True, +# 'commercial_rev_ceiling': 10, +# 'commercial_rev_share': 10, +# 'commercial_use': True, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'currency': MockERC20, +# 'derivative_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_approval': False, +# 'derivatives_attribution': True, +# 'derivatives_reciprocal': True, +# 'expiration': 0, +# 'default_minting_fee': 0, +# 'royalty_policy': ROYALTY_POLICY, +# 'transferable': True, +# 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 10, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] + +# # Create two IPs +# result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=MockERC721, +# terms=license_terms_data +# ) +# result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=MockERC721, +# terms=license_terms_data +# ) + +# ip_ids = [result1['ip_id'], result2['ip_id']] +# license_terms_id = result1['license_terms_ids'][0] + +# # Register group and add IPs +# result3 = story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=100, +# ip_ids=ip_ids, +# license_data={ +# 'license_terms_id': license_terms_id, +# 'license_template': PIL_LICENSE_TEMPLATE, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 10, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) + +# group_ip_id = result3['group_id'] + +# # Create derivative IPs +# result4 = story_client.IPAsset.mint_and_register_ip_and_make_derivative( +# spg_nft_contract=MockERC721, +# parent_ip_ids=[group_ip_id], +# license_terms_ids=[license_terms_id], +# license_template=PIL_LICENSE_TEMPLATE, +# max_minting_fee=0, +# max_rts=10, +# max_revenue_share=0 +# ) + +# child_ip_id1 = result4['ip_id'] + +# result5 = story_client.IPAsset.mint_and_register_ip_and_make_derivative( +# spg_nft_contract=MockERC721, +# parent_ip_ids=[group_ip_id], +# license_terms_ids=[license_terms_id], +# license_template=PIL_LICENSE_TEMPLATE, +# max_minting_fee=0, +# max_rts=10, +# max_revenue_share=0 +# ) + +# child_ip_id2 = result5['ip_id'] + +# # Pay royalties from child IPs to group IP +# story_client.Royalty.pay_royalty_on_behalf( +# receiver_ip_id=child_ip_id1, +# payer_ip_id=group_ip_id, +# token=MockERC20, +# amount=100 +# ) + +# story_client.Royalty.pay_royalty_on_behalf( +# receiver_ip_id=child_ip_id2, +# payer_ip_id=group_ip_id, +# token=MockERC20, +# amount=100 +# ) + +# # Transfer to vault +# story_client.Royalty.transfer_to_vault( +# royalty_policy="LRP", +# ip_id=child_ip_id1, +# ancestor_ip_id=group_ip_id, +# token=MockERC20 +# ) + +# story_client.Royalty.transfer_to_vault( +# royalty_policy="LRP", +# ip_id=child_ip_id2, +# ancestor_ip_id=group_ip_id, +# token=MockERC20 +# ) + +# return { +# 'group_ip_id': group_ip_id, +# 'ip_ids': ip_ids +# } + +# def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): +# group_ip_id = setup_royalty_collection['group_ip_id'] +# ip_ids = setup_royalty_collection['ip_ids'] + +# response = story_client.Group.collect_and_distribute_group_royalties( +# group_ip_id=group_ip_id, +# currency_tokens=[MockERC20], +# member_ip_ids=ip_ids +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'collected_royalties' in response +# assert len(response['collected_royalties']) > 0 +# assert response['collected_royalties'][0]['amount'] == 20 + +# assert 'royalties_distributed' in response +# assert len(response['royalties_distributed']) == 2 +# assert response['royalties_distributed'][0]['amount'] == 10 +# assert response['royalties_distributed'][1]['amount'] == 10 From 4549e4cd724e214bc8af2669cd44c57d5daa52f0 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Sat, 12 Apr 2025 21:26:28 +0900 Subject: [PATCH 05/26] Added royalty policy lrp constant for integration test --- tests/integration/setup_for_integration.py | 6 ++++-- tests/integration/utils.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/setup_for_integration.py b/tests/integration/setup_for_integration.py index 63bced2..aca15c5 100644 --- a/tests/integration/setup_for_integration.py +++ b/tests/integration/setup_for_integration.py @@ -28,7 +28,8 @@ generate_cid, WIP_TOKEN_ADDRESS, setup_royalty_vault, - EVEN_SPLIT_GROUP_POOL + EVEN_SPLIT_GROUP_POOL, + ROYALTY_POLICY_LRP ) # Load environment variables @@ -88,5 +89,6 @@ def story_client_2(): 'wallet_address_2', 'private_key', 'private_key_2', - 'EVEN_SPLIT_GROUP_POOL' + 'EVEN_SPLIT_GROUP_POOL', + 'ROYALTY_POLICY_LRP' ] \ No newline at end of file diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 29498bc..3cda671 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -20,6 +20,7 @@ PIL_LICENSE_TEMPLATE="0x2E896b0b2Fdb7457499B56AAaA4AE55BCB4Cd316" ARBITRATION_POLICY_UMA="0xfFD98c3877B8789124f02C7E8239A4b0Ef11E936" EVEN_SPLIT_GROUP_POOL="0xf96f2c30b41Cb6e0290de43C8528ae83d4f33F89" +ROYALTY_POLICY_LRP="0x9156e603C949481883B1d3355c6f1132D191fC41" def get_story_client_in_sepolia(web3: Web3, account) -> StoryClient: chain_id = 11155111 # Sepolia chain ID From 96d5016751d3eaa631490cb73971e4727aa8f353 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Sat, 12 Apr 2025 21:26:50 +0900 Subject: [PATCH 06/26] Added register_group_and_attach_license() --- .../resources/Group.py | 209 +++++++++--------- tests/integration/test_integration_group.py | 56 ++--- 2 files changed, 137 insertions(+), 128 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 106bd1a..f890695 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -39,7 +39,7 @@ def __init__(self, web3: Web3, account, chain_id: int): self.pi_license_template_client = PILicenseTemplateClient(web3) self.license_terms_util = LicenseTerms(web3) - self.sign = Sign(web3, account, chain_id) + self.sign_util = Sign(web3, self.chain_id, self.account) def register_group(self, group_pool: str, tx_options: dict = None) -> dict: """ @@ -71,7 +71,6 @@ def register_group(self, group_pool: str, tx_options: dict = None) -> dict: except Exception as e: raise ValueError(f"Failed to register group: {str(e)}") - def register_group_and_attach_license( self, group_pool: str, @@ -112,104 +111,118 @@ def register_group_and_attach_license( except Exception as e: raise ValueError(f"Failed to register group and attach license: {str(e)}") - # def mint_and_register_ip_and_attach_license_and_add_to_group( - # self, - # group_id: str, - # spg_nft_contract: str, - # license_data: list, - # max_allowed_reward_share: int, - # ip_metadata: dict = None, - # recipient: str = None, - # allow_duplicates: bool = True, - # deadline: int = None, - # tx_options: dict = None - # ) -> dict: - # """ - # Mint an NFT from a SPGNFT collection, register it with metadata as an IP, - # attach license terms to the registered IP, and add it to a group IP. + def mint_and_register_ip_and_attach_license_and_add_to_group( + self, + group_id: str, + spg_nft_contract: str, + license_data: list, + max_allowed_reward_share: int, + ip_metadata: dict = None, + recipient: str = None, + allow_duplicates: bool = True, + deadline: int = None, + tx_options: dict = None + ) -> dict: + """ + Mint an NFT from a SPGNFT collection, register it with metadata as an IP, + attach license terms to the registered IP, and add it to a group IP. - # :param group_id str: The ID of the group to add the IP to. - # :param spg_nft_contract str: The address of the SPG NFT contract. - # :param license_data list: List of license data objects with terms and config. - # :param max_allowed_reward_share int: Maximum allowed reward share percentage. - # :param ip_metadata dict: [Optional] The metadata for the IP. - # :param recipient str: [Optional] The recipient of the NFT (defaults to caller). - # :param allow_duplicates bool: [Optional] Whether to allow duplicate IPs. - # :param deadline int: [Optional] The deadline for the signature in milliseconds. - # :param tx_options dict: [Optional] The transaction options. - # :return dict: A dictionary with the transaction hash, IP ID, and token ID. - # """ - # try: - # if not Web3.is_address(group_id): - # raise ValueError(f'Group ID "{group_id}" is invalid.') - - # if not Web3.is_address(spg_nft_contract): - # raise ValueError(f'SPG NFT contract address "{spg_nft_contract}" is invalid.') - - # is_registered = self.ip_asset_registry_client.isRegistered(group_id) - # if not is_registered: - # raise ValueError(f"Group IP {group_id} is not registered.") - - # # Get IP account state - # ip_account = IPAccountImplClient(self.web3, group_id) - # state = ip_account.state() - - # # Calculate deadline - # calculated_deadline = self.sign._get_deadline(deadline=deadline) - - # # Get permission signature for adding to group - # sig_add_to_group = self.sign.get_permission_signature( - # ip_id=group_id, - # deadline=calculated_deadline, - # state=state, - # permissions=[{ - # 'signer': self.grouping_workflows_client.contract.address, - # 'to': self.grouping_module_client.contract.address, - # 'permission': 1, # ALLOW - # 'func': "addIp(address,address,uint256)" - # }] - # ) - - # # Process license data - # licenses_data = self._get_license_data(license_data) - - # # Process IP metadata - # metadata = self._get_ip_metadata(ip_metadata) - - # # Set recipient to caller if not provided - # if not recipient: - # recipient = self.account.address - - # response = build_and_send_transaction( - # self.web3, - # self.account, - # self.grouping_workflows_client.build_mintAndRegisterIpAndAttachLicenseAndAddToGroup_transaction, - # spg_nft_contract, - # recipient, - # metadata, - # licenses_data, - # group_id, - # self.license_terms_util.get_revenue_share(max_allowed_reward_share), - # allow_duplicates, - # { - # 'signer': self.account.address, - # 'deadline': calculated_deadline, - # 'signature': sig_add_to_group - # }, - # tx_options=tx_options - # ) + :param group_id str: The ID of the group to add the IP to. + :param spg_nft_contract str: The address of the SPG NFT contract. + :param license_data list: List of license data objects with terms and config. + :param max_allowed_reward_share int: Maximum allowed reward share percentage. + :param ip_metadata dict: [Optional] The metadata for the IP. + :param recipient str: [Optional] The recipient of the NFT (defaults to caller). + :param allow_duplicates bool: [Optional] Whether to allow duplicate IPs. + :param deadline int: [Optional] The deadline for the signature in milliseconds. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash, IP ID, and token ID. + """ + try: + if not Web3.is_address(group_id): + raise ValueError(f'Group ID "{group_id}" is invalid.') + + if not Web3.is_address(spg_nft_contract): + raise ValueError(f'SPG NFT contract address "{spg_nft_contract}" is invalid.') + + is_registered = self.ip_asset_registry_client.isRegistered(group_id) + if not is_registered: + raise ValueError(f"Group IP {group_id} is not registered.") + + # Get IP account state + ip_account = IPAccountImplClient(self.web3, contract_address=group_id) + state = ip_account.state() + + print("State:", state) + + # Calculate deadline + calculated_deadline = self.sign_util.get_deadline(deadline=deadline) + + # Get permission signature for adding to group + sig_add_to_group = self.sign_util.get_permission_signature( + ip_id=group_id, + deadline=calculated_deadline, + state=state, + permissions=[{ + 'ipId': group_id, + 'signer': self.grouping_workflows_client.contract.address, + 'to': self.grouping_module_client.contract.address, + 'permission': 1, # ALLOW + 'func': "addIp(address,address[])" + }] + ) + + # Process license data + licenses_data = self._get_license_data(license_data) + + # Process IP metadata + metadata = self._get_ip_metadata(ip_metadata) + + # Set recipient to caller if not provided + if not recipient: + recipient = self.account.address + + print("SPG NFT Contract:", spg_nft_contract) + print("Group ID:", group_id) + print("Recipient:", recipient) + print("Licenses Data:", licenses_data) + print("Metadata:", metadata) + print("Signature Info:", { + 'signer': self.account.address, + 'deadline': calculated_deadline, + 'signature': self.web3.to_bytes(hexstr=sig_add_to_group["signature"]) + }) + print("Allow Duplicates:", allow_duplicates) + + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_workflows_client.build_mintAndRegisterIpAndAttachLicenseAndAddToGroup_transaction, + spg_nft_contract, + group_id, + recipient, + licenses_data, + metadata, + { + 'signer': self.account.address, + 'deadline': calculated_deadline, + 'signature': self.web3.to_bytes(hexstr=sig_add_to_group["signature"]) + }, + allow_duplicates, + tx_options=tx_options + ) - # # Parse events to get IP ID and token ID - # registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) + # Parse events to get IP ID and token ID + registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) - # return { - # 'tx_hash': response['tx_hash'], - # 'ip_id': registration_data['ip_id'], - # 'token_id': registration_data['token_id'] - # } + return { + 'tx_hash': response['tx_hash'], + 'ip_id': registration_data['ip_id'], + 'token_id': registration_data['token_id'] + } - # except Exception as e: - # raise ValueError(f"Failed to mint and register IP and attach license and add to group: {str(e)}") + except Exception as e: + raise ValueError(f"Failed to mint and register IP and attach license and add to group: {str(e)}") # def register_ip_and_attach_license_and_add_to_group( # self, @@ -511,8 +524,6 @@ def _get_license_data(self, license_data: list) -> list: except Exception as e: raise ValueError(f"Licensing config validation failed: {str(e)}") - print(f"Original licensing_config: {licensing_config}") - # Convert to camelCase for contract interaction camelcase_config = { 'isSet': licensing_config.get('is_set', True), @@ -533,8 +544,6 @@ def _get_license_data(self, license_data: list) -> list: result.append(processed_item) - print(f"\n=== DEBUG: Final result from _get_license_data: {result} ===") - return result def _get_ip_metadata(self, ip_metadata: dict = None) -> dict: diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 0e30163..de51ee2 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -11,9 +11,9 @@ MockERC721, MockERC20, ZERO_ADDRESS, - ROYALTY_POLICY, PIL_LICENSE_TEMPLATE, - EVEN_SPLIT_GROUP_POOL + EVEN_SPLIT_GROUP_POOL, + ROYALTY_POLICY_LRP ) # class TestGroupBasicOperations: @@ -52,7 +52,7 @@ def ip_with_license(self, story_client, nft_collection): terms=[{ 'terms': { 'transferable': True, - 'royalty_policy': ROYALTY_POLICY, + 'royalty_policy': ROYALTY_POLICY_LRP, 'default_minting_fee': 0, 'expiration': 1000, 'commercial_use': True, @@ -139,33 +139,33 @@ def group_with_license(self, story_client, ip_with_license): def test_register_group_and_attach_license(self, group_with_license): assert group_with_license is not None - # def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license): - # response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( - # group_id=group_with_license, - # spg_nft_contract=MockERC721, - # license_data=[{ - # 'license_terms_id': ip_with_license['license_terms_id'], - # 'licensing_config': { - # 'is_set': True, - # 'minting_fee': 0, - # 'hook_data': ZERO_ADDRESS, - # 'licensing_hook': ZERO_ADDRESS, - # 'commercial_rev_share': 0, - # 'disabled': False, - # 'expect_minimum_group_reward_share': 0, - # 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - # } - # }], - # max_allowed_reward_share=5 - # ) + def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): + response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( + group_id=group_with_license, + spg_nft_contract=nft_collection, + license_data=[{ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }], + max_allowed_reward_share=5 + ) - # assert 'tx_hash' in response - # assert isinstance(response['tx_hash'], str) - # assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 - # assert 'ip_id' in response - # assert isinstance(response['ip_id'], str) - # assert response['ip_id'].startswith("0x") + assert 'ip_id' in response + assert isinstance(response['ip_id'], str) + assert response['ip_id'].startswith("0x") # class TestAdvancedGroupOperations: # @pytest.fixture(scope="module") From 14c55f3ffc9c033cb2c2e962eddf1e7d99118023 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Sun, 13 Apr 2025 16:04:24 +0900 Subject: [PATCH 07/26] Updated abis for grouping module and workflows --- .../DerivativeWorkflows_client.py | 31 - .../GroupingModule/GroupingModule_client.py | 8 +- .../GroupingWorkflows_client.py | 24 +- .../abi/jsons/GroupingModule.json | 1322 +++++++++-------- .../abi/jsons/GroupingWorkflows.json | 1297 ++++++++++------ 5 files changed, 1515 insertions(+), 1167 deletions(-) diff --git a/src/story_protocol_python_sdk/abi/DerivativeWorkflows/DerivativeWorkflows_client.py b/src/story_protocol_python_sdk/abi/DerivativeWorkflows/DerivativeWorkflows_client.py index 111b7b8..ababa9a 100644 --- a/src/story_protocol_python_sdk/abi/DerivativeWorkflows/DerivativeWorkflows_client.py +++ b/src/story_protocol_python_sdk/abi/DerivativeWorkflows/DerivativeWorkflows_client.py @@ -21,35 +21,4 @@ def __init__(self, web3: Web3): with open(abi_path, 'r') as abi_file: abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) - - def mintAndRegisterIpAndMakeDerivative(self, spgNftContract, derivData, ipMetadata, recipient, allowDuplicates): - return self.contract.functions.mintAndRegisterIpAndMakeDerivative(spgNftContract, derivData, ipMetadata, recipient, allowDuplicates).transact() - - def build_mintAndRegisterIpAndMakeDerivative_transaction(self, spgNftContract, derivData, ipMetadata, recipient, allowDuplicates, tx_params): - return self.contract.functions.mintAndRegisterIpAndMakeDerivative(spgNftContract, derivData, ipMetadata, recipient, allowDuplicates).build_transaction(tx_params) - - def mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(self, spgNftContract, licenseTokenIds, royaltyContext, maxRts, ipMetadata, recipient, allowDuplicates): - return self.contract.functions.mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(spgNftContract, licenseTokenIds, royaltyContext, maxRts, ipMetadata, recipient, allowDuplicates).transact() - - def build_mintAndRegisterIpAndMakeDerivativeWithLicenseTokens_transaction(self, spgNftContract, licenseTokenIds, royaltyContext, maxRts, ipMetadata, recipient, allowDuplicates, tx_params): - return self.contract.functions.mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(spgNftContract, licenseTokenIds, royaltyContext, maxRts, ipMetadata, recipient, allowDuplicates).build_transaction(tx_params) - - def multicall(self, data): - return self.contract.functions.multicall(data).transact() - - def build_multicall_transaction(self, data, tx_params): - return self.contract.functions.multicall(data).build_transaction(tx_params) - - def registerIpAndMakeDerivative(self, nftContract, tokenId, derivData, ipMetadata, sigMetadataAndRegister): - return self.contract.functions.registerIpAndMakeDerivative(nftContract, tokenId, derivData, ipMetadata, sigMetadataAndRegister).transact() - - def build_registerIpAndMakeDerivative_transaction(self, nftContract, tokenId, derivData, ipMetadata, sigMetadataAndRegister, tx_params): - return self.contract.functions.registerIpAndMakeDerivative(nftContract, tokenId, derivData, ipMetadata, sigMetadataAndRegister).build_transaction(tx_params) - - def registerIpAndMakeDerivativeWithLicenseTokens(self, nftContract, tokenId, licenseTokenIds, royaltyContext, maxRts, ipMetadata, sigMetadataAndRegister): - return self.contract.functions.registerIpAndMakeDerivativeWithLicenseTokens(nftContract, tokenId, licenseTokenIds, royaltyContext, maxRts, ipMetadata, sigMetadataAndRegister).transact() - - def build_registerIpAndMakeDerivativeWithLicenseTokens_transaction(self, nftContract, tokenId, licenseTokenIds, royaltyContext, maxRts, ipMetadata, sigMetadataAndRegister, tx_params): - return self.contract.functions.registerIpAndMakeDerivativeWithLicenseTokens(nftContract, tokenId, licenseTokenIds, royaltyContext, maxRts, ipMetadata, sigMetadataAndRegister).build_transaction(tx_params) - \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py b/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py index 2dab14e..01f3150 100644 --- a/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py +++ b/src/story_protocol_python_sdk/abi/GroupingModule/GroupingModule_client.py @@ -22,11 +22,11 @@ def __init__(self, web3: Web3): abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) - def addIp(self, groupIpId, ipIds): - return self.contract.functions.addIp(groupIpId, ipIds).transact() + def addIp(self, groupIpId, ipIds, maxAllowedRewardShare): + return self.contract.functions.addIp(groupIpId, ipIds, maxAllowedRewardShare).transact() - def build_addIp_transaction(self, groupIpId, ipIds, tx_params): - return self.contract.functions.addIp(groupIpId, ipIds).build_transaction(tx_params) + def build_addIp_transaction(self, groupIpId, ipIds, maxAllowedRewardShare, tx_params): + return self.contract.functions.addIp(groupIpId, ipIds, maxAllowedRewardShare).build_transaction(tx_params) def registerGroup(self, groupPool): return self.contract.functions.registerGroup(groupPool).transact() diff --git a/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py b/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py index 6f6c725..04db79b 100644 --- a/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py +++ b/src/story_protocol_python_sdk/abi/GroupingWorkflows/GroupingWorkflows_client.py @@ -28,11 +28,11 @@ def collectRoyaltiesAndClaimReward(self, groupIpId, currencyTokens, memberIpIds) def build_collectRoyaltiesAndClaimReward_transaction(self, groupIpId, currencyTokens, memberIpIds, tx_params): return self.contract.functions.collectRoyaltiesAndClaimReward(groupIpId, currencyTokens, memberIpIds).build_transaction(tx_params) - def mintAndRegisterIpAndAttachLicenseAndAddToGroup(self, spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates): - return self.contract.functions.mintAndRegisterIpAndAttachLicenseAndAddToGroup(spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates).transact() + def mintAndRegisterIpAndAttachLicenseAndAddToGroup(self, spgNftContract, groupId, recipient, maxAllowedRewardShare, licensesData, ipMetadata, sigAddToGroup, allowDuplicates): + return self.contract.functions.mintAndRegisterIpAndAttachLicenseAndAddToGroup(spgNftContract, groupId, recipient, maxAllowedRewardShare, licensesData, ipMetadata, sigAddToGroup, allowDuplicates).transact() - def build_mintAndRegisterIpAndAttachLicenseAndAddToGroup_transaction(self, spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates, tx_params): - return self.contract.functions.mintAndRegisterIpAndAttachLicenseAndAddToGroup(spgNftContract, groupId, recipient, licensesData, ipMetadata, sigAddToGroup, allowDuplicates).build_transaction(tx_params) + def build_mintAndRegisterIpAndAttachLicenseAndAddToGroup_transaction(self, spgNftContract, groupId, recipient, maxAllowedRewardShare, licensesData, ipMetadata, sigAddToGroup, allowDuplicates, tx_params): + return self.contract.functions.mintAndRegisterIpAndAttachLicenseAndAddToGroup(spgNftContract, groupId, recipient, maxAllowedRewardShare, licensesData, ipMetadata, sigAddToGroup, allowDuplicates).build_transaction(tx_params) def registerGroupAndAttachLicense(self, groupPool, licenseData): return self.contract.functions.registerGroupAndAttachLicense(groupPool, licenseData).transact() @@ -40,16 +40,16 @@ def registerGroupAndAttachLicense(self, groupPool, licenseData): def build_registerGroupAndAttachLicense_transaction(self, groupPool, licenseData, tx_params): return self.contract.functions.registerGroupAndAttachLicense(groupPool, licenseData).build_transaction(tx_params) - def registerGroupAndAttachLicenseAndAddIps(self, groupPool, ipIds, licenseData): - return self.contract.functions.registerGroupAndAttachLicenseAndAddIps(groupPool, ipIds, licenseData).transact() + def registerGroupAndAttachLicenseAndAddIps(self, groupPool, ipIds, maxAllowedRewardShare, licenseData): + return self.contract.functions.registerGroupAndAttachLicenseAndAddIps(groupPool, ipIds, maxAllowedRewardShare, licenseData).transact() - def build_registerGroupAndAttachLicenseAndAddIps_transaction(self, groupPool, ipIds, licenseData, tx_params): - return self.contract.functions.registerGroupAndAttachLicenseAndAddIps(groupPool, ipIds, licenseData).build_transaction(tx_params) + def build_registerGroupAndAttachLicenseAndAddIps_transaction(self, groupPool, ipIds, maxAllowedRewardShare, licenseData, tx_params): + return self.contract.functions.registerGroupAndAttachLicenseAndAddIps(groupPool, ipIds, maxAllowedRewardShare, licenseData).build_transaction(tx_params) - def registerIpAndAttachLicenseAndAddToGroup(self, nftContract, tokenId, groupId, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup): - return self.contract.functions.registerIpAndAttachLicenseAndAddToGroup(nftContract, tokenId, groupId, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup).transact() + def registerIpAndAttachLicenseAndAddToGroup(self, nftContract, tokenId, groupId, maxAllowedRewardShare, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup): + return self.contract.functions.registerIpAndAttachLicenseAndAddToGroup(nftContract, tokenId, groupId, maxAllowedRewardShare, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup).transact() - def build_registerIpAndAttachLicenseAndAddToGroup_transaction(self, nftContract, tokenId, groupId, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup, tx_params): - return self.contract.functions.registerIpAndAttachLicenseAndAddToGroup(nftContract, tokenId, groupId, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup).build_transaction(tx_params) + def build_registerIpAndAttachLicenseAndAddToGroup_transaction(self, nftContract, tokenId, groupId, maxAllowedRewardShare, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup, tx_params): + return self.contract.functions.registerIpAndAttachLicenseAndAddToGroup(nftContract, tokenId, groupId, maxAllowedRewardShare, licensesData, ipMetadata, sigMetadataAndAttachAndConfig, sigAddToGroup).build_transaction(tx_params) \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/jsons/GroupingModule.json b/src/story_protocol_python_sdk/abi/jsons/GroupingModule.json index 4a15d47..3de9243 100644 --- a/src/story_protocol_python_sdk/abi/jsons/GroupingModule.json +++ b/src/story_protocol_python_sdk/abi/jsons/GroupingModule.json @@ -1,1003 +1,1051 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "accessController", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipAssetRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licenseRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licenseToken", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "groupNFT", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "royaltyModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "disputeModule", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" }, { - "type": "function", - "name": "ACCESS_CONTROLLER", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IAccessController" + "internalType": "address", + "name": "ipAccount", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessControlled__NotIpAccount", + "type": "error" }, { - "type": "function", - "name": "DISPUTE_MODULE", "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IDisputeModule" - } - ], - "stateMutability": "view" + "name": "AccessControlled__ZeroAddress", + "type": "error" }, { - "type": "function", - "name": "GROUP_IP_ASSET_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IGroupIPAssetRegistry" + "internalType": "address", + "name": "authority", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedInvalidAuthority", + "type": "error" }, { - "type": "function", - "name": "GROUP_NFT", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IGroupNFT" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "IP_ASSET_REGISTRY", - "inputs": [], - "outputs": [ + "internalType": "address", + "name": "caller", + "type": "address" + }, { - "name": "", - "type": "address", - "internalType": "contract IIPAssetRegistry" + "internalType": "uint32", + "name": "delay", + "type": "uint32" } ], - "stateMutability": "view" + "name": "AccessManagedRequiredDelay", + "type": "error" }, { - "type": "function", - "name": "LICENSE_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract ILicenseRegistry" + "internalType": "address", + "name": "caller", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedUnauthorized", + "type": "error" }, { - "type": "function", - "name": "LICENSE_TOKEN", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract ILicenseToken" + "internalType": "address", + "name": "target", + "type": "address" } ], - "stateMutability": "view" + "name": "AddressEmptyCode", + "type": "error" }, { - "type": "function", - "name": "ROYALTY_MODULE", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IRoyaltyModule" + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "stateMutability": "view" + "name": "ERC1967InvalidImplementation", + "type": "error" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" + "name": "ERC1967NonPayable", + "type": "error" }, { - "type": "function", - "name": "__ProtocolPausable_init", - "inputs": [ - { - "name": "accessManager", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" + "inputs": [], + "name": "EnforcedPause", + "type": "error" }, { - "type": "function", - "name": "addIp", - "inputs": [ - { - "name": "groupIpId", - "type": "address", - "internalType": "address" - }, - { - "name": "ipIds", - "type": "address[]", - "internalType": "address[]" - } - ], - "outputs": [], - "stateMutability": "nonpayable" + "inputs": [], + "name": "ExpectedPause", + "type": "error" }, { - "type": "function", - "name": "authority", "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" + "name": "FailedCall", + "type": "error" }, { - "type": "function", - "name": "claimReward", "inputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" - }, - { - "name": "token", - "type": "address", - "internalType": "address" - }, - { - "name": "ipIds", - "type": "address[]", - "internalType": "address[]" + "internalType": "address", + "name": "ipId", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__CannotAddDisputedIpToGroup", + "type": "error" }, { - "type": "function", - "name": "collectRoyalties", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "token", - "type": "address", - "internalType": "address" - } - ], - "outputs": [ - { - "name": "royalties", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "childGroupId", + "type": "address" } ], - "stateMutability": "nonpayable" + "name": "GroupingModule__CannotAddGroupToGroup", + "type": "error" }, { - "type": "function", - "name": "getClaimableReward", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" - }, - { - "name": "token", - "type": "address", - "internalType": "address" - }, - { - "name": "ipIds", - "type": "address[]", - "internalType": "address[]" + "type": "address" } ], - "outputs": [ - { - "name": "", - "type": "uint256[]", - "internalType": "uint256[]" - } - ], - "stateMutability": "view" + "name": "GroupingModule__DisputedGroupCannotAddIp", + "type": "error" }, { - "type": "function", - "name": "initialize", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__DisputedGroupCannotClaimReward", + "type": "error" }, { - "type": "function", - "name": "isConsumingScheduledOp", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bytes4", - "internalType": "bytes4" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "view" + "name": "GroupingModule__DisputedGroupCannotCollectRoyalties", + "type": "error" }, { - "type": "function", - "name": "name", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "string", - "internalType": "string" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "pure" - }, - { - "type": "function", - "name": "pause", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__GroupFrozenDueToAlreadyMintLicenseTokens", + "type": "error" }, { - "type": "function", - "name": "paused", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bool", - "internalType": "bool" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "view" + "name": "GroupingModule__GroupFrozenDueToHasDerivativeIps", + "type": "error" }, { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "view" + "name": "GroupingModule__GroupIPHasNoLicenseTerms", + "type": "error" }, { - "type": "function", - "name": "registerGroup", "inputs": [ { - "name": "groupPool", - "type": "address", - "internalType": "address" - } - ], - "outputs": [ - { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "name": "GroupingModule__GroupIPLicenseHasNotSpecifyRevenueToken", + "type": "error" }, { - "type": "function", - "name": "removeIp", "inputs": [ { - "name": "groupIpId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "groupId", + "type": "address" }, { - "name": "ipIds", - "type": "address[]", - "internalType": "address[]" + "internalType": "address", + "name": "groupRewardPool", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__GroupRewardPoolNotWhitelisted", + "type": "error" }, { - "type": "function", - "name": "setAuthority", "inputs": [ { - "name": "newAuthority", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "groupNFT", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__InvalidGroupNFT", + "type": "error" }, { - "type": "function", - "name": "supportsInterface", "inputs": [ { - "name": "interfaceId", - "type": "bytes4", - "internalType": "bytes4" - } - ], - "outputs": [ + "internalType": "address", + "name": "groupId", + "type": "address" + }, { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "unpause", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ + "internalType": "address", + "name": "ipId", + "type": "address" + }, { - "name": "newImplementation", - "type": "address", - "internalType": "address" + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" }, { - "name": "data", - "type": "bytes", - "internalType": "bytes" + "internalType": "uint256", + "name": "expectGroupRewardShare", + "type": "uint256" } ], - "outputs": [], - "stateMutability": "payable" + "name": "GroupingModule__IpExpectedShareExceedsMaxAllowedShare", + "type": "error" }, { - "type": "function", - "name": "whitelistGroupRewardPool", "inputs": [ { - "name": "rewardPool", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "groupId", + "type": "address" }, { - "name": "allowed", - "type": "bool", - "internalType": "bool" + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "GroupingModule__MaxAllowedRewardShareExceeds100Percent", + "type": "error" }, { - "type": "event", - "name": "AddedIpToGroup", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { - "name": "ipIds", - "type": "address[]", - "indexed": false, - "internalType": "address[]" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "AuthorityUpdated", - "inputs": [ - { - "name": "authority", - "type": "address", - "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "royaltyToken", + "type": "address" } ], - "anonymous": false + "name": "GroupingModule__RoyaltyTokenNotWhitelisted", + "type": "error" }, { - "type": "event", - "name": "ClaimedReward", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "token", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { - "name": "ipId", - "type": "address[]", - "indexed": false, - "internalType": "address[]" + "internalType": "address", + "name": "groupCurrentToken", + "type": "address" }, { - "name": "amount", - "type": "uint256[]", - "indexed": false, - "internalType": "uint256[]" + "internalType": "address", + "name": "token", + "type": "address" } ], - "anonymous": false + "name": "GroupingModule__TokenNotMatchGroupRevenueToken", + "type": "error" }, { - "type": "event", - "name": "CollectedRoyaltiesToGroupPool", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { - "name": "token", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "uint256", + "name": "totalGroupRewardShare", + "type": "uint256" }, { - "name": "pool", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" }, { - "name": "amount", - "type": "uint256", - "indexed": false, - "internalType": "uint256" + "internalType": "uint256", + "name": "expectGroupRewardShare", + "type": "uint256" } ], - "anonymous": false + "name": "GroupingModule__TotalGroupRewardShareExceeds100Percent", + "type": "error" }, { - "type": "event", - "name": "IPGroupRegistered", - "inputs": [ - { - "name": "groupId", - "type": "address", - "indexed": true, - "internalType": "address" - }, + "inputs": [], + "name": "GroupingModule__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroGroupNFT", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroGroupRewardPool", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroIpAssetRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroLicenseRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroLicenseToken", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingModule__ZeroRoyaltyModule", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ { - "name": "groupPool", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" } ], - "anonymous": false + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" }, { - "type": "event", - "name": "Initialized", + "anonymous": false, "inputs": [ { - "name": "version", - "type": "uint64", + "indexed": true, + "internalType": "address", + "name": "groupId", + "type": "address" + }, + { "indexed": false, - "internalType": "uint64" + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" } ], - "anonymous": false + "name": "AddedIpToGroup", + "type": "event" }, { - "type": "event", - "name": "Paused", + "anonymous": false, "inputs": [ { - "name": "account", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "authority", + "type": "address" } ], - "anonymous": false + "name": "AuthorityUpdated", + "type": "event" }, { - "type": "event", - "name": "RemovedIpFromGroup", + "anonymous": false, "inputs": [ { + "indexed": true, + "internalType": "address", "name": "groupId", - "type": "address", + "type": "address" + }, + { "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "token", + "type": "address" }, { - "name": "ipIds", - "type": "address[]", "indexed": false, - "internalType": "address[]" + "internalType": "address[]", + "name": "ipId", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amount", + "type": "uint256[]" } ], - "anonymous": false + "name": "ClaimedReward", + "type": "event" }, { - "type": "event", - "name": "Unpaused", + "anonymous": false, "inputs": [ { - "name": "account", - "type": "address", + "indexed": true, + "internalType": "address", + "name": "groupId", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { "indexed": false, - "internalType": "address" + "internalType": "uint256", + "name": "amount", + "type": "uint256" } ], - "anonymous": false + "name": "CollectedRoyaltiesToGroupPool", + "type": "event" }, { - "type": "event", - "name": "Upgraded", + "anonymous": false, "inputs": [ { - "name": "implementation", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "groupId", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "groupPool", + "type": "address" } ], - "anonymous": false + "name": "IPGroupRegistered", + "type": "event" }, { - "type": "error", - "name": "AccessControlled__NotIpAccount", + "anonymous": false, "inputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" } - ] - }, - { - "type": "error", - "name": "AccessControlled__ZeroAddress", - "inputs": [] + ], + "name": "Initialized", + "type": "event" }, { - "type": "error", - "name": "AccessManagedInvalidAuthority", + "anonymous": false, "inputs": [ { - "name": "authority", - "type": "address", - "internalType": "address" + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" } - ] + ], + "name": "Paused", + "type": "event" }, { - "type": "error", - "name": "AccessManagedRequiredDelay", + "anonymous": false, "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "indexed": true, + "internalType": "address", + "name": "groupId", + "type": "address" }, { - "name": "delay", - "type": "uint32", - "internalType": "uint32" + "indexed": false, + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" } - ] + ], + "name": "RemovedIpFromGroup", + "type": "event" }, { - "type": "error", - "name": "AccessManagedUnauthorized", + "anonymous": false, "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" } - ] + ], + "name": "Unpaused", + "type": "event" }, { - "type": "error", - "name": "AddressEmptyCode", + "anonymous": false, "inputs": [ { - "name": "target", - "type": "address", - "internalType": "address" + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" } - ] + ], + "name": "Upgraded", + "type": "event" }, { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ + "inputs": [], + "name": "ACCESS_CONTROLLER", + "outputs": [ { - "name": "implementation", - "type": "address", - "internalType": "address" + "internalType": "contract IAccessController", + "name": "", + "type": "address" } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] + "inputs": [], + "name": "DISPUTE_MODULE", + "outputs": [ + { + "internalType": "contract IDisputeModule", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__CannotAddDisputedIpToGroup", - "inputs": [ + "inputs": [], + "name": "GROUP_IP_ASSET_REGISTRY", + "outputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" + "internalType": "contract IGroupIPAssetRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__CannotAddGroupToGroup", - "inputs": [ - { - "name": "groupId", - "type": "address", - "internalType": "address" - }, + "inputs": [], + "name": "GROUP_NFT", + "outputs": [ { - "name": "childGroupId", - "type": "address", - "internalType": "address" + "internalType": "contract IGroupNFT", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupFrozenDueToAlreadyMintLicenseTokens", - "inputs": [ + "inputs": [], + "name": "IP_ASSET_REGISTRY", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "contract IIPAssetRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupFrozenDueToHasDerivativeIps", - "inputs": [ + "inputs": [], + "name": "LICENSE_REGISTRY", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "contract ILicenseRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupIPHasMintingFee", - "inputs": [ - { - "name": "groupId", - "type": "address", - "internalType": "address" - }, - { - "name": "licenseTemplate", - "type": "address", - "internalType": "address" - }, + "inputs": [], + "name": "LICENSE_TOKEN", + "outputs": [ { - "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "internalType": "contract ILicenseToken", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupIPHasNoLicenseTerms", - "inputs": [ + "inputs": [], + "name": "ROYALTY_MODULE", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "contract IRoyaltyModule", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupIPLicenseHasNotSpecifyRevenueToken", - "inputs": [ + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "string", + "name": "", + "type": "string" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupIPShouldHasNonDefaultLicenseTerms", "inputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "accessManager", + "type": "address" } - ] + ], + "name": "__ProtocolPausable_init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__GroupRewardPoolNotWhitelisted", "inputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "groupIpId", + "type": "address" }, { - "name": "groupRewardPool", - "type": "address", - "internalType": "address" + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" } - ] + ], + "name": "addIp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__InvalidGroupNFT", - "inputs": [ + "inputs": [], + "name": "authority", + "outputs": [ { - "name": "groupNFT", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__RoyaltyTokenNotWhitelisted", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "royaltyToken", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" } - ] + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__TokenNotMatchGroupRevenueToken", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" - }, - { - "name": "groupCurrentToken", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "token", - "type": "address", - "internalType": "address" + "type": "address" } - ] + ], + "name": "collectRoyalties", + "outputs": [ + { + "internalType": "uint256", + "name": "royalties", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__TotalGroupRewardShareExceeds100Percent", "inputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "totalGroupRewardShare", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "token", + "type": "address" }, { - "name": "ipId", - "type": "address", - "internalType": "address" - }, + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" + } + ], + "name": "getClaimableReward", + "outputs": [ { - "name": "expectGroupRewardShare", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroAccessManager", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "accessManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroGroupNFT", - "inputs": [] + "inputs": [], + "name": "isConsumingScheduledOp", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroGroupRewardPool", - "inputs": [] + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroIpAssetRegistry", - "inputs": [] + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroLicenseRegistry", - "inputs": [] + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroLicenseToken", - "inputs": [] + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "GroupingModule__ZeroRoyaltyModule", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "groupPool", + "type": "address" + } + ], + "name": "registerGroup", + "outputs": [ + { + "internalType": "address", + "name": "groupId", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "groupIpId", + "type": "address" + }, + { + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" + } + ], + "name": "removeIp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "NotInitializing", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "newAuthority", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", "inputs": [ { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "rewardPool", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" } - ] + ], + "name": "whitelistGroupRewardPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } -] +] \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/jsons/GroupingWorkflows.json b/src/story_protocol_python_sdk/abi/jsons/GroupingWorkflows.json index bc4ee64..18d56b7 100644 --- a/src/story_protocol_python_sdk/abi/jsons/GroupingWorkflows.json +++ b/src/story_protocol_python_sdk/abi/jsons/GroupingWorkflows.json @@ -1,985 +1,1316 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "accessController", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "coreMetadataModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "groupingModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "groupNft", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipAssetRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licenseRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licensingModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "pilTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "royaltyModule", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AccessManagedInvalidAuthority", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "AccessManagedRequiredDelay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "AccessManagedUnauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "GroupingWorkflows__CallerNotSigner", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingWorkflows__NoLicenseData", + "type": "error" + }, + { + "inputs": [], + "name": "GroupingWorkflows__ZeroAddressParam", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "inputs": [], + "name": "Workflow__CallerNotAuthorizedToMint", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AuthorityUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" }, { - "type": "function", - "name": "ACCESS_CONTROLLER", "inputs": [], + "name": "ACCESS_CONTROLLER", "outputs": [ { + "internalType": "contract IAccessController", "name": "", - "type": "address", - "internalType": "contract IAccessController" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "CORE_METADATA_MODULE", "inputs": [], + "name": "CORE_METADATA_MODULE", "outputs": [ { + "internalType": "contract ICoreMetadataModule", "name": "", - "type": "address", - "internalType": "contract ICoreMetadataModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "GROUPING_MODULE", "inputs": [], + "name": "GROUPING_MODULE", "outputs": [ { + "internalType": "contract IGroupingModule", "name": "", - "type": "address", - "internalType": "contract IGroupingModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "GROUP_NFT", "inputs": [], + "name": "GROUP_NFT", "outputs": [ { + "internalType": "contract GroupNFT", "name": "", - "type": "address", - "internalType": "contract GroupNFT" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "IP_ASSET_REGISTRY", "inputs": [], + "name": "IP_ASSET_REGISTRY", "outputs": [ { + "internalType": "contract IIPAssetRegistry", "name": "", - "type": "address", - "internalType": "contract IIPAssetRegistry" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "LICENSE_REGISTRY", "inputs": [], + "name": "LICENSE_REGISTRY", "outputs": [ { + "internalType": "contract ILicenseRegistry", "name": "", - "type": "address", - "internalType": "contract ILicenseRegistry" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "LICENSING_MODULE", "inputs": [], + "name": "LICENSING_MODULE", "outputs": [ { + "internalType": "contract ILicensingModule", "name": "", - "type": "address", - "internalType": "contract ILicensingModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "PIL_TEMPLATE", "inputs": [], + "name": "PIL_TEMPLATE", "outputs": [ { + "internalType": "contract IPILicenseTemplate", "name": "", - "type": "address", - "internalType": "contract IPILicenseTemplate" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "ROYALTY_MODULE", "inputs": [], + "name": "ROYALTY_MODULE", "outputs": [ { + "internalType": "contract RoyaltyModule", "name": "", - "type": "address", - "internalType": "contract RoyaltyModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", "outputs": [ { + "internalType": "string", "name": "", - "type": "string", - "internalType": "string" + "type": "string" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "authority", "inputs": [], + "name": "authority", "outputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", + "inputs": [ + { + "internalType": "address", + "name": "groupIpId", + "type": "address" + }, + { + "internalType": "address[]", + "name": "currencyTokens", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "memberIpIds", + "type": "address[]" + } + ], "name": "collectRoyaltiesAndClaimReward", + "outputs": [ + { + "internalType": "uint256[]", + "name": "collectedRoyalties", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [ { + "internalType": "address", "name": "groupIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address[]", "name": "currencyTokens", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "groupSnapshotIds", + "type": "uint256[]" }, { + "internalType": "address[]", "name": "memberIpIds", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" } ], + "name": "collectRoyaltiesAndClaimReward_deprecated", "outputs": [ { + "internalType": "uint256[]", "name": "collectedRoyalties", - "type": "uint256[]", - "internalType": "uint256[]" + "type": "uint256[]" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "initialize", "inputs": [ { + "internalType": "address", "name": "accessManager", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "initialize", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "isConsumingScheduledOp", "inputs": [], + "name": "isConsumingScheduledOp", "outputs": [ { + "internalType": "bytes4", "name": "", - "type": "bytes4", - "internalType": "bytes4" + "type": "bytes4" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "mintAndRegisterIpAndAttachLicenseAndAddToGroup", "inputs": [ { + "internalType": "address", "name": "spgNftContract", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "recipient", - "type": "address", - "internalType": "address" + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" }, { - "name": "licensesData", - "type": "tuple[]", - "internalType": "struct WorkflowStructs.LicenseData[]", "components": [ { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "licensingConfig", - "type": "tuple", - "internalType": "struct Licensing.LicensingConfig", "components": [ { + "internalType": "bool", "name": "isSet", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint256", "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "address", "name": "licensingHook", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "hookData", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" }, { + "internalType": "uint32", "name": "commercialRevShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bool", "name": "disabled", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint32", "name": "expectMinimumGroupRewardShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "address", "name": "expectGroupRewardPool", - "type": "address", - "internalType": "address" + "type": "address" } - ] + ], + "internalType": "struct Licensing.LicensingConfig", + "name": "licensingConfig", + "type": "tuple" } - ] + ], + "internalType": "struct WorkflowStructs.LicenseData[]", + "name": "licensesData", + "type": "tuple[]" }, { - "name": "ipMetadata", - "type": "tuple", - "internalType": "struct WorkflowStructs.IPMetadata", "components": [ { + "internalType": "string", "name": "ipMetadataURI", - "type": "string", - "internalType": "string" + "type": "string" }, { + "internalType": "bytes32", "name": "ipMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" }, { + "internalType": "string", "name": "nftMetadataURI", - "type": "string", - "internalType": "string" + "type": "string" }, { + "internalType": "bytes32", "name": "nftMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } - ] + ], + "internalType": "struct WorkflowStructs.IPMetadata", + "name": "ipMetadata", + "type": "tuple" }, { - "name": "sigAddToGroup", - "type": "tuple", - "internalType": "struct WorkflowStructs.SignatureData", "components": [ { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "deadline", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "bytes", "name": "signature", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } - ] + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigAddToGroup", + "type": "tuple" }, { + "internalType": "bool", "name": "allowDuplicates", - "type": "bool", - "internalType": "bool" + "type": "bool" } ], + "name": "mintAndRegisterIpAndAttachLicenseAndAddToGroup", "outputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "tokenId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "multicall", "inputs": [ { + "internalType": "address", + "name": "spgNftContract", + "type": "address" + }, + { + "internalType": "address", + "name": "groupId", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "licenseTemplate", + "type": "address" + }, + { + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "string", + "name": "ipMetadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "ipMetadataHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "nftMetadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "nftMetadataHash", + "type": "bytes32" + } + ], + "internalType": "struct WorkflowStructs.IPMetadata", + "name": "ipMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigAddToGroup", + "type": "tuple" + } + ], + "name": "mintAndRegisterIpAndAttachLicenseAndAddToGroup_deprecated", + "outputs": [ + { + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", "name": "data", - "type": "bytes[]", - "internalType": "bytes[]" + "type": "bytes[]" } ], + "name": "multicall", "outputs": [ { + "internalType": "bytes[]", "name": "results", - "type": "bytes[]", - "internalType": "bytes[]" + "type": "bytes[]" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "onERC721Received", "inputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "bytes", "name": "", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "onERC721Received", "outputs": [ { + "internalType": "bytes4", "name": "", - "type": "bytes4", - "internalType": "bytes4" + "type": "bytes4" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "proxiableUUID", "inputs": [], + "name": "proxiableUUID", "outputs": [ { + "internalType": "bytes32", "name": "", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "registerGroupAndAttachLicense", "inputs": [ { + "internalType": "address", "name": "groupPool", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "licenseData", - "type": "tuple", - "internalType": "struct WorkflowStructs.LicenseData", "components": [ { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "licensingConfig", - "type": "tuple", - "internalType": "struct Licensing.LicensingConfig", "components": [ { + "internalType": "bool", "name": "isSet", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint256", "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "address", "name": "licensingHook", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "hookData", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" }, { + "internalType": "uint32", "name": "commercialRevShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bool", "name": "disabled", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint32", "name": "expectMinimumGroupRewardShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "address", "name": "expectGroupRewardPool", - "type": "address", - "internalType": "address" + "type": "address" } - ] + ], + "internalType": "struct Licensing.LicensingConfig", + "name": "licensingConfig", + "type": "tuple" } - ] + ], + "internalType": "struct WorkflowStructs.LicenseData", + "name": "licenseData", + "type": "tuple" } ], + "name": "registerGroupAndAttachLicense", "outputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "registerGroupAndAttachLicenseAndAddIps", "inputs": [ { + "internalType": "address", "name": "groupPool", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address[]", "name": "ipIds", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" }, { - "name": "licenseData", - "type": "tuple", - "internalType": "struct WorkflowStructs.LicenseData", "components": [ { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "licensingConfig", - "type": "tuple", - "internalType": "struct Licensing.LicensingConfig", "components": [ { + "internalType": "bool", "name": "isSet", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint256", "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "address", "name": "licensingHook", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "hookData", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" }, { + "internalType": "uint32", "name": "commercialRevShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bool", "name": "disabled", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint32", "name": "expectMinimumGroupRewardShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "address", "name": "expectGroupRewardPool", - "type": "address", - "internalType": "address" + "type": "address" } - ] + ], + "internalType": "struct Licensing.LicensingConfig", + "name": "licensingConfig", + "type": "tuple" } - ] + ], + "internalType": "struct WorkflowStructs.LicenseData", + "name": "licenseData", + "type": "tuple" } ], + "name": "registerGroupAndAttachLicenseAndAddIps", "outputs": [ { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "groupPool", + "type": "address" + }, + { + "internalType": "address[]", + "name": "ipIds", + "type": "address[]" + }, + { + "internalType": "address", + "name": "licenseTemplate", + "type": "address" + }, + { + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + } + ], + "name": "registerGroupAndAttachLicenseAndAddIps_deprecated", + "outputs": [ + { + "internalType": "address", + "name": "groupId", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "groupPool", + "type": "address" + }, + { + "internalType": "address", + "name": "licenseTemplate", + "type": "address" + }, + { + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + } + ], + "name": "registerGroupAndAttachLicense_deprecated", + "outputs": [ + { + "internalType": "address", + "name": "groupId", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "registerIpAndAttachLicenseAndAddToGroup", "inputs": [ { + "internalType": "address", "name": "nftContract", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "tokenId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "address", "name": "groupId", - "type": "address", - "internalType": "address" + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxAllowedRewardShare", + "type": "uint256" }, { - "name": "licensesData", - "type": "tuple[]", - "internalType": "struct WorkflowStructs.LicenseData[]", "components": [ { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "licensingConfig", - "type": "tuple", - "internalType": "struct Licensing.LicensingConfig", "components": [ { + "internalType": "bool", "name": "isSet", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint256", "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "address", "name": "licensingHook", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "hookData", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" }, { + "internalType": "uint32", "name": "commercialRevShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bool", "name": "disabled", - "type": "bool", - "internalType": "bool" + "type": "bool" }, { + "internalType": "uint32", "name": "expectMinimumGroupRewardShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "address", "name": "expectGroupRewardPool", - "type": "address", - "internalType": "address" + "type": "address" } - ] + ], + "internalType": "struct Licensing.LicensingConfig", + "name": "licensingConfig", + "type": "tuple" } - ] + ], + "internalType": "struct WorkflowStructs.LicenseData[]", + "name": "licensesData", + "type": "tuple[]" }, { - "name": "ipMetadata", - "type": "tuple", - "internalType": "struct WorkflowStructs.IPMetadata", "components": [ { + "internalType": "string", "name": "ipMetadataURI", - "type": "string", - "internalType": "string" + "type": "string" }, { + "internalType": "bytes32", "name": "ipMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" }, { + "internalType": "string", "name": "nftMetadataURI", - "type": "string", - "internalType": "string" + "type": "string" }, { + "internalType": "bytes32", "name": "nftMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } - ] + ], + "internalType": "struct WorkflowStructs.IPMetadata", + "name": "ipMetadata", + "type": "tuple" }, { - "name": "sigMetadataAndAttachAndConfig", - "type": "tuple", - "internalType": "struct WorkflowStructs.SignatureData", "components": [ { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "deadline", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "bytes", "name": "signature", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } - ] + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigMetadataAndAttachAndConfig", + "type": "tuple" }, { - "name": "sigAddToGroup", - "type": "tuple", - "internalType": "struct WorkflowStructs.SignatureData", "components": [ { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "deadline", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { + "internalType": "bytes", "name": "signature", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } - ] + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigAddToGroup", + "type": "tuple" } ], + "name": "registerIpAndAttachLicenseAndAddToGroup", "outputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setAuthority", - "inputs": [ - { - "name": "newAuthority", - "type": "address", - "internalType": "address" + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "upgradeToAndCall", "inputs": [ { - "name": "newImplementation", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "nftContract", + "type": "address" }, { - "name": "data", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "event", - "name": "AuthorityUpdated", - "inputs": [ + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, { - "name": "authority", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ + "internalType": "address", + "name": "groupId", + "type": "address" + }, { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ + "internalType": "address", + "name": "licenseTemplate", + "type": "address" + }, { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AccessManagedInvalidAuthority", - "inputs": [ + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + }, { - "name": "authority", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AccessManagedRequiredDelay", - "inputs": [ + "components": [ + { + "internalType": "string", + "name": "ipMetadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "ipMetadataHash", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "nftMetadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "nftMetadataHash", + "type": "bytes32" + } + ], + "internalType": "struct WorkflowStructs.IPMetadata", + "name": "ipMetadata", + "type": "tuple" + }, { - "name": "caller", - "type": "address", - "internalType": "address" + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigMetadataAndAttachAndConfig", + "type": "tuple" }, { - "name": "delay", - "type": "uint32", - "internalType": "uint32" + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct WorkflowStructs.SignatureData", + "name": "sigAddToGroup", + "type": "tuple" } - ] - }, - { - "type": "error", - "name": "AccessManagedUnauthorized", - "inputs": [ + ], + "name": "registerIpAndAttachLicenseAndAddToGroup_deprecated", + "outputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" } - ] + ], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AddressEmptyCode", "inputs": [ { - "name": "target", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "newAuthority", + "type": "address" } - ] + ], + "name": "setAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "ERC1967InvalidImplementation", "inputs": [ { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "FailedCall", - "inputs": [] - }, - { - "type": "error", - "name": "GroupingWorkflows__NoLicenseData", - "inputs": [] - }, - { - "type": "error", - "name": "GroupingWorkflows__ZeroAddressParam", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "bytes", + "name": "data", + "type": "bytes" } - ] - }, - { - "type": "error", - "name": "Workflow__CallerNotAuthorizedToMint", - "inputs": [] + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file From 3b090849e143e8d2585bd12ff084f57f5b9f6c00 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Sun, 13 Apr 2025 16:04:52 +0900 Subject: [PATCH 08/26] buggy mint_and_register_ip_and_attach_license_and_add_to_group() --- .../resources/Group.py | 30 +++++++++---------- .../test_integration_ip_account.py | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index f890695..7d9c8b5 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -153,8 +153,6 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( ip_account = IPAccountImplClient(self.web3, contract_address=group_id) state = ip_account.state() - print("State:", state) - # Calculate deadline calculated_deadline = self.sign_util.get_deadline(deadline=deadline) @@ -172,27 +170,26 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( }] ) - # Process license data licenses_data = self._get_license_data(license_data) - # Process IP metadata metadata = self._get_ip_metadata(ip_metadata) + max_allowed_reward_share = self.license_terms_util.get_revenue_share(max_allowed_reward_share) + # Set recipient to caller if not provided if not recipient: recipient = self.account.address - - print("SPG NFT Contract:", spg_nft_contract) - print("Group ID:", group_id) - print("Recipient:", recipient) - print("Licenses Data:", licenses_data) - print("Metadata:", metadata) - print("Signature Info:", { - 'signer': self.account.address, - 'deadline': calculated_deadline, - 'signature': self.web3.to_bytes(hexstr=sig_add_to_group["signature"]) - }) - print("Allow Duplicates:", allow_duplicates) + # Print relevant information before transaction + print("=== Mint and Register IP and Attach License and Add to Group ===") + print(f"Group ID: {group_id}") + print(f"SPG NFT Contract: {spg_nft_contract}") + print(f"Recipient: {recipient}") + print(f"Max Allowed Reward Share: {max_allowed_reward_share}") + print(f"License Data: {licenses_data}") + print(f"Metadata: {metadata}") + print(f"Signature Details: {{'signer': '{self.account.address}', 'deadline': {calculated_deadline}, 'signature': {self.web3.to_bytes(hexstr=sig_add_to_group['signature'])}}}") + print(f"Allow Duplicates: {allow_duplicates}") + print(f"Transaction Options: {tx_options}") response = build_and_send_transaction( self.web3, @@ -201,6 +198,7 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( spg_nft_contract, group_id, recipient, + max_allowed_reward_share, licenses_data, metadata, { diff --git a/tests/integration/test_integration_ip_account.py b/tests/integration/test_integration_ip_account.py index 4e190e9..b15c47d 100644 --- a/tests/integration/test_integration_ip_account.py +++ b/tests/integration/test_integration_ip_account.py @@ -422,7 +422,7 @@ def test_execute_with_sig_wrong_signer(self, story_client): ) ip_id = register_response['ipId'] - deadline = getBlockTimestamp(web3) + 100 + deadline = get_block_timestamp(web3) + 100 state = story_client.IPAccount.getIpAccountNonce(ip_id) data = "0x" From 6729f0bfa519b89568097a5d5fc20dbfee22fded Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Tue, 15 Apr 2025 02:21:42 +0900 Subject: [PATCH 09/26] Added register_group_and_attach_license_and_add_ips() and respective integration test --- .../resources/Group.py | 372 ++++++++-------- tests/integration/test_integration_group.py | 397 +++++++++--------- 2 files changed, 393 insertions(+), 376 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 7d9c8b5..aadc709 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -166,7 +166,7 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( 'signer': self.grouping_workflows_client.contract.address, 'to': self.grouping_module_client.contract.address, 'permission': 1, # ALLOW - 'func': "addIp(address,address[])" + 'func': "addIp(address,address[])", }] ) @@ -222,205 +222,209 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( except Exception as e: raise ValueError(f"Failed to mint and register IP and attach license and add to group: {str(e)}") - # def register_ip_and_attach_license_and_add_to_group( - # self, - # group_id: str, - # nft_contract: str, - # token_id: int, - # license_data: list, - # max_allowed_reward_share: int, - # ip_metadata: dict = None, - # deadline: int = None, - # tx_options: dict = None - # ) -> dict: - # """ - # Register an NFT as IP with metadata, attach license terms to the registered IP, - # and add it to a group IP. - - # :param group_id str: The ID of the group to add the IP to. - # :param nft_contract str: The address of the NFT contract. - # :param token_id int: The token ID of the NFT. - # :param license_data list: List of license data objects with terms and config. - # :param max_allowed_reward_share int: Maximum allowed reward share percentage. - # :param ip_metadata dict: [Optional] The metadata for the IP. - # :param deadline int: [Optional] The deadline for the signature in milliseconds. - # :param tx_options dict: [Optional] The transaction options. - # :return dict: A dictionary with the transaction hash, IP ID, and token ID. - # """ - # try: - # if not Web3.is_address(group_id): - # raise ValueError(f'Group ID "{group_id}" is invalid.') - - # if not Web3.is_address(nft_contract): - # raise ValueError(f'NFT contract address "{nft_contract}" is invalid.') - - # # Check if group is registered - # is_registered = self.ip_asset_registry_client.isRegistered(group_id) - # if not is_registered: - # raise ValueError(f"Group IP {group_id} is not registered.") - - # # Get IP ID for the NFT - # ip_id = self.ip_asset_registry_client.ipId( - # self.chain_id, - # nft_contract, - # token_id - # ) + def register_ip_and_attach_license_and_add_to_group( + self, + group_id: str, + nft_contract: str, + token_id: int, + license_data: list, + max_allowed_reward_share: int, + ip_metadata: dict = None, + deadline: int = None, + tx_options: dict = None + ) -> dict: + """ + Register an NFT as IP with metadata, attach license terms to the registered IP, + and add it to a group IP. + + :param group_id str: The ID of the group to add the IP to. + :param nft_contract str: The address of the NFT contract. + :param token_id int: The token ID of the NFT. + :param license_data list: List of license data objects with terms and config. + :param max_allowed_reward_share int: Maximum allowed reward share percentage. + :param ip_metadata dict: [Optional] The metadata for the IP. + :param deadline int: [Optional] The deadline for the signature in milliseconds. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash, IP ID, and token ID. + """ + try: + if not self.web3.is_address(group_id): + raise ValueError(f'Group ID "{group_id}" is invalid.') - # # Get IP account state - # ip_account = IPAccountImplClient(self.web3, group_id) - # state = ip_account.state() - - # # Calculate deadline - # calculated_deadline = self.sign._get_deadline(deadline=deadline) - - # # Get permission signature for adding to group - # sig_add_to_group = self.sign.get_permission_signature( - # ip_id=group_id, - # deadline=calculated_deadline, - # state=state, - # permissions=[{ - # 'signer': self.grouping_workflows_client.contract.address, - # 'to': self.grouping_module_client.contract.address, - # 'permission': 1, # ALLOW - # 'func': "addIp(address,address,uint256)" - # }] - # ) + if not self.web3.is_address(nft_contract): + raise ValueError(f'NFT contract address "{nft_contract}" is invalid.') - # # Get permission signature for metadata and license - # sig_metadata_and_attach = self.sign.get_permission_signature( - # ip_id=ip_id, - # deadline=calculated_deadline, - # state="0x0000000000000000000000000000000000000000000000000000000000000000", - # permissions=[ - # { - # 'signer': self.grouping_workflows_client.contract.address, - # 'to': self.core_metadata_module_client.contract.address, - # 'permission': 1, # ALLOW - # 'func': "setAll(address,string,bytes32,bytes32)" - # }, - # { - # 'signer': self.grouping_workflows_client.contract.address, - # 'to': self.licensing_module_client.contract.address, - # 'permission': 1, # ALLOW - # 'func': "attachLicenseTerms(address,address,uint256)" - # }, - # { - # 'signer': self.grouping_workflows_client.contract.address, - # 'to': self.licensing_module_client.contract.address, - # 'permission': 1, # ALLOW - # 'func': "setLicensingConfig(address,uint256,tuple)" - # } - # ] - # ) + # Check if group is registered + is_registered = self.ip_asset_registry_client.isRegistered(group_id) + if not is_registered: + raise ValueError(f"Group IP {group_id} is not registered.") - # # Process license data - # licenses_data = self._get_license_data(license_data) + # Get IP ID for the NFT + ip_id = self.ip_asset_registry_client.ipId( + self.chain_id, + nft_contract, + token_id + ) - # # Process IP metadata - # metadata = self._get_ip_metadata(ip_metadata) + # Get IP account state + ip_account = IPAccountImplClient(self.web3, group_id) + state = ip_account.state() - # response = build_and_send_transaction( - # self.web3, - # self.account, - # self.grouping_workflows_client.build_registerIpAndAttachLicenseAndAddToGroup_transaction, - # nft_contract, - # token_id, - # group_id, - # metadata, - # licenses_data, - # self.license_terms_util.get_revenue_share(max_allowed_reward_share), - # { - # 'signer': self.account.address, - # 'deadline': calculated_deadline, - # 'signature': sig_add_to_group - # }, - # { - # 'signer': self.account.address, - # 'deadline': calculated_deadline, - # 'signature': sig_metadata_and_attach - # }, - # tx_options=tx_options - # ) + # Calculate deadline + calculated_deadline = self.sign_util.get_deadline(deadline=deadline) + + # Get permission signature for adding to group + sig_add_to_group = self.sign_util.get_permission_signature( + ip_id=group_id, + deadline=calculated_deadline, + state=state, + permissions=[{ + 'ipId': group_id, + 'signer': self.grouping_workflows_client.contract.address, + 'to': self.grouping_module_client.contract.address, + 'permission': 1, # ALLOW + 'func': "addIp(address,address[])" + }] + ) - # # Parse events to get IP ID and token ID - # registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) + # Get permission signature for metadata and license + sig_metadata_and_attach = self.sign_util.get_permission_signature( + ip_id=ip_id, + deadline=calculated_deadline, + state=self.web3.to_bytes(hexstr=ZERO_HASH), + permissions=[ + { + 'ipId': ip_id, + 'signer': self.grouping_workflows_client.contract.address, + 'to': self.core_metadata_module_client.contract.address, + 'permission': 1, # ALLOW + 'func': "setAll(address,string,bytes32,bytes32)" + }, + { + 'ipId': ip_id, + 'signer': self.grouping_workflows_client.contract.address, + 'to': self.licensing_module_client.contract.address, + 'permission': 1, # ALLOW + 'func': "attachLicenseTerms(address,address,uint256)" + }, + { + 'ipId': ip_id, + 'signer': self.grouping_workflows_client.contract.address, + 'to': self.licensing_module_client.contract.address, + 'permission': 1, # ALLOW + 'func': "setLicensingConfig(address,uint256,tuple)" + } + ] + ) - # return { - # 'tx_hash': response['tx_hash'], - # 'ip_id': registration_data['ip_id'], - # 'token_id': registration_data['token_id'] - # } + # Process license data + licenses_data = self._get_license_data(license_data) - # except Exception as e: - # raise ValueError(f"Failed to register IP and attach license and add to group: {str(e)}") + # Process IP metadata + metadata = self._get_ip_metadata(ip_metadata) + + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_workflows_client.build_registerIpAndAttachLicenseAndAddToGroup_transaction, + nft_contract, + token_id, + group_id, + self.license_terms_util.get_revenue_share(max_allowed_reward_share), + licenses_data, + metadata, + { + 'signer': self.account.address, + 'deadline': calculated_deadline, + 'signature': self.web3.to_bytes(hexstr=sig_metadata_and_attach['signature']) + }, + { + 'signer': self.account.address, + 'deadline': calculated_deadline, + 'signature': self.web3.to_bytes(hexstr=sig_add_to_group['signature']) + }, + tx_options=tx_options + ) + + # Parse events to get IP ID and token ID + registration_data = self._parse_tx_ip_registered_event(response['tx_receipt']) + + return { + 'tx_hash': response['tx_hash'], + 'ip_id': registration_data['ip_id'], + 'token_id': registration_data['token_id'] + } + + except Exception as e: + raise ValueError(f"Failed to register IP and attach license and add to group: {str(e)}") - # def register_group_and_attach_license_and_add_ips( - # self, - # group_pool: str, - # ip_ids: list, - # license_data: dict, - # max_allowed_reward_share: int, - # tx_options: dict = None - # ) -> dict: - # """ - # Register a group IP with a group reward pool, attach license terms to the group IP, - # and add individual IPs to the group IP. + def register_group_and_attach_license_and_add_ips( + self, + group_pool: str, + ip_ids: list, + license_data: dict, + max_allowed_reward_share: int, + tx_options: dict = None + ) -> dict: + """ + Register a group IP with a group reward pool, attach license terms to the group IP, + and add individual IPs to the group IP. - # :param group_pool str: The address of the group pool. - # :param ip_ids list: List of IP IDs to add to the group. - # :param license_data dict: License data object with terms and config. - # :param max_allowed_reward_share int: Maximum allowed reward share percentage. - # :param tx_options dict: [Optional] The transaction options. - # :return dict: A dictionary with the transaction hash and group ID. - # """ - # try: - # if not Web3.is_address(group_pool): - # raise ValueError(f'Group pool address "{group_pool}" is invalid.') + :param group_pool str: The address of the group pool. + :param ip_ids list: List of IP IDs to add to the group. + :param license_data dict: License data object with terms and config. + :param max_allowed_reward_share int: Maximum allowed reward share percentage. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash and group ID. + """ + try: + if not self.web3.is_address(group_pool): + raise ValueError(f'Group pool address "{group_pool}" is invalid.') - # # Validate IP IDs - # for ip_id in ip_ids: - # if not Web3.is_address(ip_id): - # raise ValueError(f'IP ID "{ip_id}" is invalid.') + # Validate IP IDs + for ip_id in ip_ids: + if not self.web3.is_address(ip_id): + raise ValueError(f'IP ID "{ip_id}" is invalid.') - # is_registered = self.ip_asset_registry_client.isRegistered(ip_id) - # if not is_registered: - # raise ValueError(f"IP {ip_id} is not registered.") - - # # Process license data - # license_data_processed = self._get_license_data([license_data])[0] - - # # Check if license terms are attached to all IPs - # for ip_id in ip_ids: - # is_attached = self.license_registry_client.hasIpAttachedLicenseTerms( - # ip_id, - # license_data_processed['licenseTemplate'], - # license_data_processed['licenseTermsId'] - # ) - # if not is_attached: - # raise ValueError( - # f"License terms must be attached to IP {ip_id} before adding to group." - # ) + is_registered = self.ip_asset_registry_client.isRegistered(ip_id) + if not is_registered: + raise ValueError(f"IP {ip_id} is not registered.") - # response = build_and_send_transaction( - # self.web3, - # self.account, - # self.grouping_workflows_client.build_registerGroupAndAttachLicenseAndAddIps_transaction, - # group_pool, - # ip_ids, - # license_data_processed, - # self.license_terms_util.get_revenue_share(max_allowed_reward_share), - # tx_options=tx_options - # ) + # Process license data + license_data_processed = self._get_license_data([license_data])[0] - # group_id = self._parse_tx_ip_group_registered_event(response['tx_receipt']) + # Check if license terms are attached to all IPs + for ip_id in ip_ids: + is_attached = self.license_registry_client.hasIpAttachedLicenseTerms( + ip_id, + license_data_processed['licenseTemplate'], + license_data_processed['licenseTermsId'] + ) + if not is_attached: + raise ValueError( + f"License terms must be attached to IP {ip_id} before adding to group." + ) - # return { - # 'tx_hash': response['tx_hash'], - # 'group_id': group_id - # } + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_workflows_client.build_registerGroupAndAttachLicenseAndAddIps_transaction, + group_pool, + ip_ids, + self.license_terms_util.get_revenue_share(max_allowed_reward_share), + license_data_processed, + tx_options=tx_options + ) - # except Exception as e: - # raise ValueError(f"Failed to register group and attach license and add IPs: {str(e)}") + group_id = self._parse_tx_ip_group_registered_event(response['tx_receipt']) + + return { + 'tx_hash': response['tx_hash'], + 'group_id': group_id + } + + except Exception as e: + raise ValueError(f"Failed to register group and attach license and add IPs: {str(e)}") # def collect_and_distribute_group_royalties( # self, diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index de51ee2..09d0de9 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -30,7 +30,144 @@ # assert isinstance(response['group_id'], str) # assert response['group_id'].startswith("0x") -class TestGroupWithLicenseOperations: +# class TestGroupWithLicenseOperations: +# @pytest.fixture(scope="module") +# def nft_collection(self, story_client): +# tx_data = story_client.NFTClient.create_nft_collection( +# name="test-collection", +# symbol="TEST", +# max_supply=100, +# is_public_minting=True, +# mint_open=True, +# contract_uri="test-uri", +# mint_fee_recipient=account.address, +# ) +# return tx_data['nft_contract'] + +# @pytest.fixture(scope="module") +# def ip_with_license(self, story_client, nft_collection): +# # Create initial IP with license terms +# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=[{ +# 'terms': { +# 'transferable': True, +# 'royalty_policy': ROYALTY_POLICY_LRP, +# 'default_minting_fee': 0, +# 'expiration': 1000, +# 'commercial_use': True, +# 'commercial_attribution': False, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'commercial_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_attribution': True, +# 'derivatives_approval': False, +# 'derivatives_reciprocal': True, +# 'derivative_rev_ceiling': 0, +# 'currency': MockERC20, +# 'uri': "test case" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) + +# ip_id = response['ip_id'] +# license_terms_id = response['license_terms_ids'][0] + +# licensing_config = { +# 'isSet': True, +# 'mintingFee': 0, +# 'licensingHook': ZERO_ADDRESS, +# 'hookData': ZERO_ADDRESS, +# 'commercialRevShare': 0, +# 'disabled': False, +# 'expectMinimumGroupRewardShare': 0, +# 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL +# } + +# # Set licensing config +# story_client.License.set_licensing_config( +# ip_id=ip_id, +# license_terms_id=license_terms_id, +# license_template=PIL_LICENSE_TEMPLATE, +# licensing_config=licensing_config +# ) + +# return { +# 'ip_id': ip_id, +# 'license_terms_id': license_terms_id +# } + +# @pytest.fixture(scope="module") +# def group_with_license(self, story_client, ip_with_license): +# response = story_client.Group.register_group_and_attach_license( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) + +# assert response is not None +# assert 'group_id' in response +# assert response['group_id'] is not None +# return response['group_id'] + +# def test_register_group_and_attach_license(self, group_with_license): +# assert group_with_license is not None + +# def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): +# response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( +# group_id=group_with_license, +# spg_nft_contract=nft_collection, +# license_data=[{ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }], +# max_allowed_reward_share=5 +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'ip_id' in response +# assert isinstance(response['ip_id'], str) +# assert response['ip_id'].startswith("0x") + +class TestAdvancedGroupOperations: @pytest.fixture(scope="module") def nft_collection(self, story_client): tx_data = story_client.NFTClient.create_nft_collection( @@ -43,10 +180,9 @@ def nft_collection(self, story_client): mint_fee_recipient=account.address, ) return tx_data['nft_contract'] - + @pytest.fixture(scope="module") def ip_with_license(self, story_client, nft_collection): - # Create initial IP with license terms response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( spg_nft_contract=nft_collection, terms=[{ @@ -82,37 +218,54 @@ def ip_with_license(self, story_client, nft_collection): }] ) - ip_id = response['ip_id'] - license_terms_id = response['license_terms_ids'][0] - - licensing_config = { - 'isSet': True, - 'mintingFee': 0, - 'licensingHook': ZERO_ADDRESS, - 'hookData': ZERO_ADDRESS, - 'commercialRevShare': 0, - 'disabled': False, - 'expectMinimumGroupRewardShare': 0, - 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL - } - - # Set licensing config - story_client.License.set_licensing_config( - ip_id=ip_id, - license_terms_id=license_terms_id, - license_template=PIL_LICENSE_TEMPLATE, - licensing_config=licensing_config - ) - return { - 'ip_id': ip_id, - 'license_terms_id': license_terms_id + 'ip_id': response['ip_id'], + 'license_terms_id': response['license_terms_ids'][0] } @pytest.fixture(scope="module") - def group_with_license(self, story_client, ip_with_license): - response = story_client.Group.register_group_and_attach_license( + def group_id(self, story_client): + response = story_client.Group.register_group( + group_pool=EVEN_SPLIT_GROUP_POOL + ) + return response['group_id'] + + # def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): + # token_id = get_token_id(MockERC721, story_client.web3, story_client.account) + + # response = story_client.Group.register_ip_and_attach_license_and_add_to_group( + # group_id=group_id, + # nft_contract=MockERC721, + # token_id=token_id, + # max_allowed_reward_share=5, + # license_data=[{ + # 'license_terms_id': ip_with_license['license_terms_id'], + # 'licensing_config': { + # 'is_set': True, + # 'minting_fee': 0, + # 'hook_data': ZERO_ADDRESS, + # 'licensing_hook': ZERO_ADDRESS, + # 'commercial_rev_share': 0, + # 'disabled': False, + # 'expect_minimum_group_reward_share': 0, + # 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + # } + # }] + # ) + + # assert 'tx_hash' in response + # assert isinstance(response['tx_hash'], str) + # assert len(response['tx_hash']) > 0 + + # assert 'ip_id' in response + # assert isinstance(response['ip_id'], str) + # assert response['ip_id'].startswith("0x") + + def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): + response = story_client.Group.register_group_and_attach_license_and_add_ips( group_pool=EVEN_SPLIT_GROUP_POOL, + max_allowed_reward_share=5, + ip_ids=[ip_with_license['ip_id']], license_data={ 'license_terms_id': ip_with_license['license_terms_id'], 'licensing_config': { @@ -127,175 +280,35 @@ def group_with_license(self, story_client, ip_with_license): } } ) - - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - - assert response is not None - assert 'group_id' in response - assert response['group_id'] is not None - return response['group_id'] - - def test_register_group_and_attach_license(self, group_with_license): - assert group_with_license is not None - - def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): - response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( - group_id=group_with_license, - spg_nft_contract=nft_collection, - license_data=[{ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }], - max_allowed_reward_share=5 - ) assert 'tx_hash' in response assert isinstance(response['tx_hash'], str) assert len(response['tx_hash']) > 0 - assert 'ip_id' in response - assert isinstance(response['ip_id'], str) - assert response['ip_id'].startswith("0x") - -# class TestAdvancedGroupOperations: -# @pytest.fixture(scope="module") -# def ip_with_license(self, story_client): -# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=MockERC721, -# terms=[{ -# 'terms': { -# 'transferable': True, -# 'royalty_policy': ROYALTY_POLICY, -# 'default_minting_fee': 0, -# 'expiration': 1000, -# 'commercial_use': True, -# 'commercial_attribution': False, -# 'commercializer_checker': ZERO_ADDRESS, -# 'commercializer_checker_data': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'commercial_rev_ceiling': 0, -# 'derivatives_allowed': True, -# 'derivatives_attribution': True, -# 'derivatives_approval': False, -# 'derivatives_reciprocal': True, -# 'derivative_rev_ceiling': 0, -# 'currency': MockERC20, -# 'uri': "test case" -# }, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# ) - -# return { -# 'ip_id': response['ip_id'], -# 'license_terms_id': response['license_terms_ids'][0] -# } - -# @pytest.fixture(scope="module") -# def group_id(self, story_client): -# response = story_client.Group.register_group( -# group_pool=EVEN_SPLIT_GROUP_POOL -# ) -# return response['group_id'] - -# def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): -# token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - -# response = story_client.Group.register_ip_and_attach_license_and_add_to_group( -# group_id=group_id, -# nft_contract=MockERC721, -# token_id=token_id, -# max_allowed_reward_share=5, -# license_data=[{ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': "ZERO_ADDRESS", -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# ) - -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 - -# assert 'ip_id' in response -# assert isinstance(response['ip_id'], str) -# assert response['ip_id'].startswith("0x") - -# def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): -# response = story_client.Group.register_group_and_attach_license_and_add_ips( -# group_pool=EVEN_SPLIT_GROUP_POOL, -# max_allowed_reward_share=5, -# ip_ids=[ip_with_license['ip_id']], -# license_data={ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': ZERO_ADDRESS -# } -# } -# ) - -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 - -# assert 'group_id' in response -# assert isinstance(response['group_id'], str) -# assert response['group_id'].startswith("0x") + assert 'group_id' in response + assert isinstance(response['group_id'], str) + assert response['group_id'].startswith("0x") -# def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): -# with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): -# story_client.Group.register_group_and_attach_license_and_add_ips( -# group_pool=EVEN_SPLIT_GROUP_POOL, -# max_allowed_reward_share=5, -# ip_ids=[ZERO_ADDRESS], # Invalid IP address -# license_data={ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': ZERO_ADDRESS -# } -# } -# ) + def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): + with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): + story_client.Group.register_group_and_attach_license_and_add_ips( + group_pool=EVEN_SPLIT_GROUP_POOL, + max_allowed_reward_share=5, + ip_ids=[ZERO_ADDRESS], # Invalid IP address + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) # class TestCollectRoyaltyAndClaimReward: # @pytest.fixture(scope="module") From c3bd25ef00a0cd534f6c83cf5d0be30e5eaef2fe Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Tue, 15 Apr 2025 22:23:53 +0900 Subject: [PATCH 10/26] Added mint_and_register_ip_and_attach_license_and_add_to_group() and respective integration test --- .../resources/Group.py | 169 +++--- tests/integration/test_integration_group.py | 555 ++++++++++-------- 2 files changed, 390 insertions(+), 334 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index aadc709..12ce6e9 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -166,7 +166,7 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( 'signer': self.grouping_workflows_client.contract.address, 'to': self.grouping_module_client.contract.address, 'permission': 1, # ALLOW - 'func': "addIp(address,address[])", + 'func': "addIp(address,address[],uint256)", }] ) @@ -179,17 +179,6 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( # Set recipient to caller if not provided if not recipient: recipient = self.account.address - # Print relevant information before transaction - print("=== Mint and Register IP and Attach License and Add to Group ===") - print(f"Group ID: {group_id}") - print(f"SPG NFT Contract: {spg_nft_contract}") - print(f"Recipient: {recipient}") - print(f"Max Allowed Reward Share: {max_allowed_reward_share}") - print(f"License Data: {licenses_data}") - print(f"Metadata: {metadata}") - print(f"Signature Details: {{'signer': '{self.account.address}', 'deadline': {calculated_deadline}, 'signature': {self.web3.to_bytes(hexstr=sig_add_to_group['signature'])}}}") - print(f"Allow Duplicates: {allow_duplicates}") - print(f"Transaction Options: {tx_options}") response = build_and_send_transaction( self.web3, @@ -283,7 +272,7 @@ def register_ip_and_attach_license_and_add_to_group( 'signer': self.grouping_workflows_client.contract.address, 'to': self.grouping_module_client.contract.address, 'permission': 1, # ALLOW - 'func': "addIp(address,address[])" + 'func': "addIp(address,address[],uint256)", }] ) @@ -322,6 +311,24 @@ def register_ip_and_attach_license_and_add_to_group( # Process IP metadata metadata = self._get_ip_metadata(ip_metadata) + # Print all parameters before sending the transaction + print("=== Transaction Parameters ===") + print(f"NFT Contract: {nft_contract}") + print(f"Token ID: {token_id}") + print(f"Group ID: {group_id}") + print(f"Max Allowed Reward Share: {max_allowed_reward_share}") + print(f"Licenses Data: {licenses_data}") + print(f"Metadata: {metadata}") + print("Signature Metadata and Attach:") + print(f" Signer: {self.account.address}") + print(f" Deadline: {calculated_deadline}") + print(f" Signature: {self.web3.to_bytes(hexstr=sig_metadata_and_attach['signature'])}") + print("Signature Add to Group:") + print(f" Signer: {self.account.address}") + print(f" Deadline: {calculated_deadline}") + print(f" Signature: {self.web3.to_bytes(hexstr=sig_add_to_group['signature'])}") + print(f"Transaction Options: {tx_options}") + print("============================") response = build_and_send_transaction( self.web3, @@ -426,76 +433,76 @@ def register_group_and_attach_license_and_add_ips( except Exception as e: raise ValueError(f"Failed to register group and attach license and add IPs: {str(e)}") - # def collect_and_distribute_group_royalties( - # self, - # group_ip_id: str, - # currency_tokens: list, - # member_ip_ids: list, - # tx_options: dict = None - # ) -> dict: - # """ - # Collect royalties for the entire group and distribute the rewards to each member IP's royalty vault. - - # :param group_ip_id str: The ID of the group IP. - # :param currency_tokens list: List of currency token addresses. - # :param member_ip_ids list: List of member IP IDs. - # :param tx_options dict: [Optional] The transaction options. - # :return dict: A dictionary with the transaction hash and collected royalties. - # """ - # try: - # if not Web3.is_address(group_ip_id): - # raise ValueError(f'Group IP ID "{group_ip_id}" is invalid.') - - # if not currency_tokens: - # raise ValueError("At least one currency token is required.") - - # if not member_ip_ids: - # raise ValueError("At least one member IP ID is required.") - - # # Validate currency tokens - # for token in currency_tokens: - # if not Web3.is_address(token): - # raise ValueError(f'Currency token address "{token}" is invalid.') + def collect_and_distribute_group_royalties( + self, + group_ip_id: str, + currency_tokens: list, + member_ip_ids: list, + tx_options: dict = None + ) -> dict: + """ + Collect royalties for the entire group and distribute the rewards to each member IP's royalty vault. + + :param group_ip_id str: The ID of the group IP. + :param currency_tokens list: List of currency token addresses. + :param member_ip_ids list: List of member IP IDs. + :param tx_options dict: [Optional] The transaction options. + :return dict: A dictionary with the transaction hash and collected royalties. + """ + try: + if not self.web3.is_address(group_ip_id): + raise ValueError(f'Group IP ID "{group_ip_id}" is invalid.') + + if not currency_tokens: + raise ValueError("At least one currency token is required.") + + if not member_ip_ids: + raise ValueError("At least one member IP ID is required.") + + # Validate currency tokens + for token in currency_tokens: + if not self.web3.is_address(token): + raise ValueError(f'Currency token address "{token}" is invalid.') - # if token == ZERO_ADDRESS: - # raise ValueError("Currency token cannot be the zero address.") - - # # Validate group IP - # is_group_registered = self.ip_asset_registry_client.isRegistered(group_ip_id) - # if not is_group_registered: - # raise ValueError(f"The group IP with ID {group_ip_id} is not registered.") - - # # Validate member IPs - # for ip_id in member_ip_ids: - # if not Web3.is_address(ip_id): - # raise ValueError(f'Member IP ID "{ip_id}" is invalid.') + if token == ZERO_ADDRESS: + raise ValueError("Currency token cannot be the zero address.") + + # Validate group IP + is_group_registered = self.ip_asset_registry_client.isRegistered(group_ip_id) + if not is_group_registered: + raise ValueError(f"The group IP with ID {group_ip_id} is not registered.") + + # Validate member IPs + for ip_id in member_ip_ids: + if not self.web3.is_address(ip_id): + raise ValueError(f'Member IP ID "{ip_id}" is invalid.') - # is_member_registered = self.ip_asset_registry_client.isRegistered(ip_id) - # if not is_member_registered: - # raise ValueError(f"Member IP with ID {ip_id} is not registered.") - - # response = build_and_send_transaction( - # self.web3, - # self.account, - # self.grouping_workflows_client.build_collectRoyaltiesAndClaimReward_transaction, - # group_ip_id, - # currency_tokens, - # member_ip_ids, - # tx_options=tx_options - # ) - - # # Parse events to get collected royalties - # collected_royalties = self._parse_tx_collected_royalties_to_group_pool_event(response['tx_receipt']) - # royalties_distributed = self._parse_tx_royalty_paid_event(response['tx_receipt']) - - # return { - # 'tx_hash': response['tx_hash'], - # 'collected_royalties': collected_royalties, - # 'royalties_distributed': royalties_distributed - # } - - # except Exception as e: - # raise ValueError(f"Failed to collect and distribute group royalties: {str(e)}") + is_member_registered = self.ip_asset_registry_client.isRegistered(ip_id) + if not is_member_registered: + raise ValueError(f"Member IP with ID {ip_id} is not registered.") + + response = build_and_send_transaction( + self.web3, + self.account, + self.grouping_workflows_client.build_collectRoyaltiesAndClaimReward_transaction, + group_ip_id, + currency_tokens, + member_ip_ids, + tx_options=tx_options + ) + + # Parse events to get collected royalties + collected_royalties = self._parse_tx_collected_royalties_to_group_pool_event(response['tx_receipt']) + royalties_distributed = self._parse_tx_royalty_paid_event(response['tx_receipt']) + + return { + 'tx_hash': response['tx_hash'], + 'collected_royalties': collected_royalties, + 'royalties_distributed': royalties_distributed + } + + except Exception as e: + raise ValueError(f"Failed to collect and distribute group royalties: {str(e)}") def _get_license_data(self, license_data: list) -> list: """ diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 09d0de9..5baf18a 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -2,6 +2,7 @@ import pytest from web3 import Web3 +import copy from setup_for_integration import ( web3, @@ -167,7 +168,150 @@ # assert isinstance(response['ip_id'], str) # assert response['ip_id'].startswith("0x") -class TestAdvancedGroupOperations: +# class TestAdvancedGroupOperations: +# @pytest.fixture(scope="module") +# def nft_collection(self, story_client): +# tx_data = story_client.NFTClient.create_nft_collection( +# name="test-collection", +# symbol="TEST", +# max_supply=100, +# is_public_minting=True, +# mint_open=True, +# contract_uri="test-uri", +# mint_fee_recipient=account.address, +# ) +# return tx_data['nft_contract'] + +# @pytest.fixture(scope="module") +# def ip_with_license(self, story_client, nft_collection): +# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=[{ +# 'terms': { +# 'transferable': True, +# 'royalty_policy': ROYALTY_POLICY_LRP, +# 'default_minting_fee': 0, +# 'expiration': 1000, +# 'commercial_use': True, +# 'commercial_attribution': False, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'commercial_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_attribution': True, +# 'derivatives_approval': False, +# 'derivatives_reciprocal': True, +# 'derivative_rev_ceiling': 0, +# 'currency': MockERC20, +# 'uri': "test case" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) + +# return { +# 'ip_id': response['ip_id'], +# 'license_terms_id': response['license_terms_ids'][0] +# } + +# @pytest.fixture(scope="module") +# def group_id(self, story_client): +# response = story_client.Group.register_group( +# group_pool=EVEN_SPLIT_GROUP_POOL +# ) +# return response['group_id'] + +# def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): +# token_id = get_token_id(MockERC721, story_client.web3, story_client.account) + +# response = story_client.Group.register_ip_and_attach_license_and_add_to_group( +# group_id=group_id, +# nft_contract=MockERC721, +# token_id=token_id, +# max_allowed_reward_share=5, +# license_data=[{ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) + +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 + +# assert 'ip_id' in response +# assert isinstance(response['ip_id'], str) +# assert response['ip_id'].startswith("0x") + +# # def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): +# # response = story_client.Group.register_group_and_attach_license_and_add_ips( +# # group_pool=EVEN_SPLIT_GROUP_POOL, +# # max_allowed_reward_share=5, +# # ip_ids=[ip_with_license['ip_id']], +# # license_data={ +# # 'license_terms_id': ip_with_license['license_terms_id'], +# # 'licensing_config': { +# # 'is_set': True, +# # 'minting_fee': 0, +# # 'hook_data': ZERO_ADDRESS, +# # 'licensing_hook': ZERO_ADDRESS, +# # 'commercial_rev_share': 0, +# # 'disabled': False, +# # 'expect_minimum_group_reward_share': 0, +# # 'expect_group_reward_pool': ZERO_ADDRESS +# # } +# # } +# # ) + +# # assert 'tx_hash' in response +# # assert isinstance(response['tx_hash'], str) +# # assert len(response['tx_hash']) > 0 + +# # assert 'group_id' in response +# # assert isinstance(response['group_id'], str) +# # assert response['group_id'].startswith("0x") + +# # def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): +# # with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): +# # story_client.Group.register_group_and_attach_license_and_add_ips( +# # group_pool=EVEN_SPLIT_GROUP_POOL, +# # max_allowed_reward_share=5, +# # ip_ids=[ZERO_ADDRESS], # Invalid IP address +# # license_data={ +# # 'license_terms_id': ip_with_license['license_terms_id'], +# # 'licensing_config': { +# # 'is_set': True, +# # 'minting_fee': 0, +# # 'hook_data': ZERO_ADDRESS, +# # 'licensing_hook': ZERO_ADDRESS, +# # 'commercial_rev_share': 0, +# # 'disabled': False, +# # 'expect_minimum_group_reward_share': 0, +# # 'expect_group_reward_pool': ZERO_ADDRESS +# # } +# # } +# # ) + +class TestCollectRoyaltyAndClaimReward: @pytest.fixture(scope="module") def nft_collection(self, story_client): tx_data = story_client.NFTClient.create_nft_collection( @@ -180,100 +324,85 @@ def nft_collection(self, story_client): mint_fee_recipient=account.address, ) return tx_data['nft_contract'] - + @pytest.fixture(scope="module") - def ip_with_license(self, story_client, nft_collection): - response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( - spg_nft_contract=nft_collection, - terms=[{ - 'terms': { - 'transferable': True, - 'royalty_policy': ROYALTY_POLICY_LRP, - 'default_minting_fee': 0, - 'expiration': 1000, - 'commercial_use': True, - 'commercial_attribution': False, - 'commercializer_checker': ZERO_ADDRESS, - 'commercializer_checker_data': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'commercial_rev_ceiling': 0, - 'derivatives_allowed': True, - 'derivatives_attribution': True, - 'derivatives_approval': False, - 'derivatives_reciprocal': True, - 'derivative_rev_ceiling': 0, - 'currency': MockERC20, - 'uri': "test case" - }, - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }] - ) + def setup_royalty_collection(self, story_client, nft_collection): + # Create license terms data + license_terms_data = [{ + 'terms': { + 'commercial_attribution': True, + 'commercial_rev_ceiling': 10, + 'commercial_rev_share': 10, + 'commercial_use': True, + 'commercializer_checker': ZERO_ADDRESS, + 'commercializer_checker_data': ZERO_ADDRESS, + 'currency': MockERC20, + 'derivative_rev_ceiling': 0, + 'derivatives_allowed': True, + 'derivatives_approval': False, + 'derivatives_attribution': True, + 'derivatives_reciprocal': True, + 'expiration': 0, + 'default_minting_fee': 0, + 'royalty_policy': ROYALTY_POLICY_LRP, + 'transferable': True, + 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" + }, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 10, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }] + # Create unique metadata for each IP + metadata_1 = { + 'ip_metadata_uri': "test-uri-1", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-1")), + 'nft_metadata_uri': "test-nft-uri-1", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-a")) + } - return { - 'ip_id': response['ip_id'], - 'license_terms_id': response['license_terms_ids'][0] + metadata_2 = { + 'ip_metadata_uri': "test-uri-2", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-2")), + 'nft_metadata_uri': "test-nft-uri-2", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-2")) } - - @pytest.fixture(scope="module") - def group_id(self, story_client): - response = story_client.Group.register_group( - group_pool=EVEN_SPLIT_GROUP_POOL + + # Create two IPs + result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=copy.deepcopy(license_terms_data), + ip_metadata=metadata_1 + ) + result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=copy.deepcopy(license_terms_data), + ip_metadata=metadata_2 ) - return response['group_id'] - - # def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): - # token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - - # response = story_client.Group.register_ip_and_attach_license_and_add_to_group( - # group_id=group_id, - # nft_contract=MockERC721, - # token_id=token_id, - # max_allowed_reward_share=5, - # license_data=[{ - # 'license_terms_id': ip_with_license['license_terms_id'], - # 'licensing_config': { - # 'is_set': True, - # 'minting_fee': 0, - # 'hook_data': ZERO_ADDRESS, - # 'licensing_hook': ZERO_ADDRESS, - # 'commercial_rev_share': 0, - # 'disabled': False, - # 'expect_minimum_group_reward_share': 0, - # 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - # } - # }] - # ) - # assert 'tx_hash' in response - # assert isinstance(response['tx_hash'], str) - # assert len(response['tx_hash']) > 0 + ip_ids = [result1['ip_id'], result2['ip_id']] + license_terms_id = result1['license_terms_ids'][0] - # assert 'ip_id' in response - # assert isinstance(response['ip_id'], str) - # assert response['ip_id'].startswith("0x") - - def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): - response = story_client.Group.register_group_and_attach_license_and_add_ips( + # Register group and add IPs + result3 = story_client.Group.register_group_and_attach_license_and_add_ips( group_pool=EVEN_SPLIT_GROUP_POOL, - max_allowed_reward_share=5, - ip_ids=[ip_with_license['ip_id']], + max_allowed_reward_share=100, + ip_ids=ip_ids, license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], + 'license_terms_id': license_terms_id, + 'license_template': PIL_LICENSE_TEMPLATE, 'licensing_config': { 'is_set': True, 'minting_fee': 0, 'hook_data': ZERO_ADDRESS, 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, + 'commercial_rev_share': 10, 'disabled': False, 'expect_minimum_group_reward_share': 0, 'expect_group_reward_pool': ZERO_ADDRESS @@ -281,186 +410,106 @@ def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_wi } ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 + group_ip_id = result3['group_id'] - assert 'group_id' in response - assert isinstance(response['group_id'], str) - assert response['group_id'].startswith("0x") - - def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): - with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): - story_client.Group.register_group_and_attach_license_and_add_ips( - group_pool=EVEN_SPLIT_GROUP_POOL, - max_allowed_reward_share=5, - ip_ids=[ZERO_ADDRESS], # Invalid IP address - license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS - } - } - ) + # Create derivative IPs - Step 1: Mint and register + result4 = story_client.IPAsset.mint_and_register_ip( + spg_nft_contract=nft_collection, + ip_metadata={ + 'ip_metadata_uri': "test-derivative-uri-4", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-1")), + 'nft_metadata_uri': "test-derivative-nft-uri-4", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-1")) + } + ) + child_ip_id1 = result4['ip_id'] -# class TestCollectRoyaltyAndClaimReward: -# @pytest.fixture(scope="module") -# def setup_royalty_collection(self, story_client): -# # Create license terms data -# license_terms_data = [{ -# 'terms': { -# 'commercial_attribution': True, -# 'commercial_rev_ceiling': 10, -# 'commercial_rev_share': 10, -# 'commercial_use': True, -# 'commercializer_checker': ZERO_ADDRESS, -# 'commercializer_checker_data': ZERO_ADDRESS, -# 'currency': MockERC20, -# 'derivative_rev_ceiling': 0, -# 'derivatives_allowed': True, -# 'derivatives_approval': False, -# 'derivatives_attribution': True, -# 'derivatives_reciprocal': True, -# 'expiration': 0, -# 'default_minting_fee': 0, -# 'royalty_policy': ROYALTY_POLICY, -# 'transferable': True, -# 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" -# }, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 10, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] - -# # Create two IPs -# result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=MockERC721, -# terms=license_terms_data -# ) -# result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=MockERC721, -# terms=license_terms_data -# ) - -# ip_ids = [result1['ip_id'], result2['ip_id']] -# license_terms_id = result1['license_terms_ids'][0] - -# # Register group and add IPs -# result3 = story_client.Group.register_group_and_attach_license_and_add_ips( -# group_pool=EVEN_SPLIT_GROUP_POOL, -# max_allowed_reward_share=100, -# ip_ids=ip_ids, -# license_data={ -# 'license_terms_id': license_terms_id, -# 'license_template': PIL_LICENSE_TEMPLATE, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 10, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': ZERO_ADDRESS -# } -# } -# ) - -# group_ip_id = result3['group_id'] - -# # Create derivative IPs -# result4 = story_client.IPAsset.mint_and_register_ip_and_make_derivative( -# spg_nft_contract=MockERC721, -# parent_ip_ids=[group_ip_id], -# license_terms_ids=[license_terms_id], -# license_template=PIL_LICENSE_TEMPLATE, -# max_minting_fee=0, -# max_rts=10, -# max_revenue_share=0 -# ) - -# child_ip_id1 = result4['ip_id'] - -# result5 = story_client.IPAsset.mint_and_register_ip_and_make_derivative( -# spg_nft_contract=MockERC721, -# parent_ip_ids=[group_ip_id], -# license_terms_ids=[license_terms_id], -# license_template=PIL_LICENSE_TEMPLATE, -# max_minting_fee=0, -# max_rts=10, -# max_revenue_share=0 -# ) - -# child_ip_id2 = result5['ip_id'] + # Step 2: Register as derivative + story_client.IPAsset.register_derivative( + child_ip_id=child_ip_id1, + parent_ip_ids=[group_ip_id], + license_terms_ids=[license_terms_id], + max_minting_fee=0, + max_rts=10, + max_revenue_share=0 + ) + + # Create second derivative IP - Step 1: Mint and register + result5 = story_client.IPAsset.mint_and_register_ip( + spg_nft_contract=nft_collection, + ip_metadata={ + 'ip_metadata_uri': "test-derivative-uri-5", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-2")), + 'nft_metadata_uri': "test-derivative-nft-uri-5", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-2")) + } + ) + child_ip_id2 = result5['ip_id'] + + # Step 2: Register as derivative + story_client.IPAsset.register_derivative( + child_ip_id=child_ip_id2, + parent_ip_ids=[group_ip_id], + license_terms_ids=[license_terms_id], + max_minting_fee=0, + max_rts=10, + max_revenue_share=0 + ) -# # Pay royalties from child IPs to group IP -# story_client.Royalty.pay_royalty_on_behalf( -# receiver_ip_id=child_ip_id1, -# payer_ip_id=group_ip_id, -# token=MockERC20, -# amount=100 -# ) + # Pay royalties from child IPs to group IP + story_client.Royalty.pay_royalty_on_behalf( + receiver_ip_id=child_ip_id1, + payer_ip_id=group_ip_id, + token=MockERC20, + amount=100 + ) -# story_client.Royalty.pay_royalty_on_behalf( -# receiver_ip_id=child_ip_id2, -# payer_ip_id=group_ip_id, -# token=MockERC20, -# amount=100 -# ) + story_client.Royalty.pay_royalty_on_behalf( + receiver_ip_id=child_ip_id2, + payer_ip_id=group_ip_id, + token=MockERC20, + amount=100 + ) -# # Transfer to vault -# story_client.Royalty.transfer_to_vault( -# royalty_policy="LRP", -# ip_id=child_ip_id1, -# ancestor_ip_id=group_ip_id, -# token=MockERC20 -# ) + # Transfer to vault + story_client.Royalty.transfer_to_vault( + royalty_policy="LRP", + ip_id=child_ip_id1, + ancestor_ip_id=group_ip_id, + token=MockERC20 + ) -# story_client.Royalty.transfer_to_vault( -# royalty_policy="LRP", -# ip_id=child_ip_id2, -# ancestor_ip_id=group_ip_id, -# token=MockERC20 -# ) + story_client.Royalty.transfer_to_vault( + royalty_policy="LRP", + ip_id=child_ip_id2, + ancestor_ip_id=group_ip_id, + token=MockERC20 + ) -# return { -# 'group_ip_id': group_ip_id, -# 'ip_ids': ip_ids -# } + return { + 'group_ip_id': group_ip_id, + 'ip_ids': ip_ids + } -# def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): -# group_ip_id = setup_royalty_collection['group_ip_id'] -# ip_ids = setup_royalty_collection['ip_ids'] + def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): + group_ip_id = setup_royalty_collection['group_ip_id'] + ip_ids = setup_royalty_collection['ip_ids'] -# response = story_client.Group.collect_and_distribute_group_royalties( -# group_ip_id=group_ip_id, -# currency_tokens=[MockERC20], -# member_ip_ids=ip_ids -# ) + response = story_client.Group.collect_and_distribute_group_royalties( + group_ip_id=group_ip_id, + currency_tokens=[MockERC20], + member_ip_ids=ip_ids + ) -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 -# assert 'collected_royalties' in response -# assert len(response['collected_royalties']) > 0 -# assert response['collected_royalties'][0]['amount'] == 20 + assert 'collected_royalties' in response + assert len(response['collected_royalties']) > 0 + assert response['collected_royalties'][0]['amount'] == 20 -# assert 'royalties_distributed' in response -# assert len(response['royalties_distributed']) == 2 -# assert response['royalties_distributed'][0]['amount'] == 10 -# assert response['royalties_distributed'][1]['amount'] == 10 + assert 'royalties_distributed' in response + assert len(response['royalties_distributed']) == 2 + assert response['royalties_distributed'][0]['amount'] == 10 + assert response['royalties_distributed'][1]['amount'] == 10 From 41bc880a746e9fe8fb3f33087a2c384de21c9d58 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Wed, 16 Apr 2025 00:19:49 +0900 Subject: [PATCH 11/26] Added transfer_to_vault() to royalty module --- .../RoyaltyPolicyLAP_client.py | 7 +++ .../resources/Royalty.py | 54 +++++++++++++++++++ .../scripts/config.json | 3 +- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/story_protocol_python_sdk/abi/RoyaltyPolicyLAP/RoyaltyPolicyLAP_client.py b/src/story_protocol_python_sdk/abi/RoyaltyPolicyLAP/RoyaltyPolicyLAP_client.py index 07131c4..cf09002 100644 --- a/src/story_protocol_python_sdk/abi/RoyaltyPolicyLAP/RoyaltyPolicyLAP_client.py +++ b/src/story_protocol_python_sdk/abi/RoyaltyPolicyLAP/RoyaltyPolicyLAP_client.py @@ -21,4 +21,11 @@ def __init__(self, web3: Web3): with open(abi_path, 'r') as abi_file: abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) + + def transferToVault(self, ipId, ancestorIpId, token): + return self.contract.functions.transferToVault(ipId, ancestorIpId, token).transact() + + def build_transferToVault_transaction(self, ipId, ancestorIpId, token, tx_params): + return self.contract.functions.transferToVault(ipId, ancestorIpId, token).build_transaction(tx_params) + \ No newline at end of file diff --git a/src/story_protocol_python_sdk/resources/Royalty.py b/src/story_protocol_python_sdk/resources/Royalty.py index abe9e94..f137d24 100644 --- a/src/story_protocol_python_sdk/resources/Royalty.py +++ b/src/story_protocol_python_sdk/resources/Royalty.py @@ -180,6 +180,60 @@ def claim_all_revenue(self, ancestor_ip_id: str, claimer: str, child_ip_ids: lis except Exception as e: raise ValueError(f"Failed to claim all revenue: {str(e)}") + def transfer_to_vault( + self, + ip_id: str, + ancestor_ip_id: str, + token: str, + royalty_policy: str = "LAP", + tx_options: dict = None + ) -> dict: + """ + Transfers to vault an amount of revenue tokens claimable via a royalty policy. + + :param ip_id str: The IP ID. + :param ancestor_ip_id str: The ancestor IP ID. + :param token str: The token address. + :param royalty_policy str: The royalty policy to use ("LAP" or "LRP"). + :param tx_options dict: [Optional] Transaction options. + :return dict: A dictionary with the transaction hash and receipt. + """ + try: + if not self.web3.is_address(token): + raise ValueError(f'Token address "{token}" is invalid.') + + # Determine which royalty policy to use + if royalty_policy == "LAP": + royalty_policy_client = self.royalty_policy_lap_client + elif royalty_policy == "LRP": + royalty_policy_client = self.royalty_policy_lap_client # Same ABI for all royalty policies + # Override the contract address for LRP + royalty_policy_client.contract.address = "0x9156e603C949481883B1d3355c6f1132D191fC41" + else: + # If it's a custom address + if not self.web3.is_address(royalty_policy): + raise ValueError(f'Royalty policy address "{royalty_policy}" is invalid.') + royalty_policy_client = self.royalty_policy_lap_client # Same ABI for all royalty policies + royalty_policy_client.contract.address = self.web3.to_checksum_address(royalty_policy) + + response = build_and_send_transaction( + self.web3, + self.account, + royalty_policy_client.build_transferToVault_transaction, + ip_id, + ancestor_ip_id, + token, + tx_options=tx_options + ) + + return { + 'tx_hash': response['tx_hash'], + 'receipt': response['tx_receipt'] + } + + except Exception as e: + raise ValueError(f"Failed to transfer to vault: {str(e)}") + def _get_claimer_info(self, claimer): """ Get information about the claimer address. diff --git a/src/story_protocol_python_sdk/scripts/config.json b/src/story_protocol_python_sdk/scripts/config.json index 1286088..66c58c8 100644 --- a/src/story_protocol_python_sdk/scripts/config.json +++ b/src/story_protocol_python_sdk/scripts/config.json @@ -109,7 +109,8 @@ "contract_address": "0xBe54FB168b3c982b7AaE60dB6CF75Bd8447b390E", "functions": [ "onRoyaltyPayment", - "getRoyaltyData" + "getRoyaltyData", + "transferToVault" ] }, { From fd0ab87847423138917d2799e52c609a9bebc427 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Wed, 16 Apr 2025 00:20:05 +0900 Subject: [PATCH 12/26] testing collect_and_distribute_group_royalties() --- tests/integration/test_integration_group.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 5baf18a..8155734 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -471,6 +471,12 @@ def setup_royalty_collection(self, story_client, nft_collection): amount=100 ) + print("claimable revenue is ", story_client.Royalty.claimable_revenue( + royalty_vault_ip_id=child_ip_id1, + claimer=account.address, + token=MockERC20 + )) + # Transfer to vault story_client.Royalty.transfer_to_vault( royalty_policy="LRP", From 89835ba6812e03be158639ba78d3cbb9065d0809 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Wed, 16 Apr 2025 12:50:00 +0900 Subject: [PATCH 13/26] Updated abi jsons --- .../abi/jsons/CoreMetadataModule.json | 646 +++---- .../abi/jsons/LicensingModule.json | 1706 +++++++++-------- 2 files changed, 1217 insertions(+), 1135 deletions(-) diff --git a/src/story_protocol_python_sdk/abi/jsons/CoreMetadataModule.json b/src/story_protocol_python_sdk/abi/jsons/CoreMetadataModule.json index 290f34c..2f71ec6 100644 --- a/src/story_protocol_python_sdk/abi/jsons/CoreMetadataModule.json +++ b/src/story_protocol_python_sdk/abi/jsons/CoreMetadataModule.json @@ -1,519 +1,497 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "accessController", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipAccountRegistry", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" }, { - "type": "function", - "name": "ACCESS_CONTROLLER", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IAccessController" + "internalType": "address", + "name": "ipAccount", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessControlled__NotIpAccount", + "type": "error" }, { - "type": "function", - "name": "IP_ASSET_REGISTRY", "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IIPAssetRegistry" - } - ], - "stateMutability": "view" + "name": "AccessControlled__ZeroAddress", + "type": "error" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "string", - "internalType": "string" + "internalType": "address", + "name": "authority", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedInvalidAuthority", + "type": "error" }, { - "type": "function", - "name": "authority", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" } ], - "stateMutability": "view" + "name": "AccessManagedRequiredDelay", + "type": "error" }, { - "type": "function", - "name": "freezeMetadata", "inputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "AccessManagedUnauthorized", + "type": "error" }, { - "type": "function", - "name": "initialize", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "target", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "AddressEmptyCode", + "type": "error" }, { - "type": "function", - "name": "isConsumingScheduledOp", "inputs": [], - "outputs": [ + "name": "CoreMetadataModule__MetadataAlreadyFrozen", + "type": "error" + }, + { + "inputs": [], + "name": "CoreMetadataModule__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "bytes4", - "internalType": "bytes4" + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "stateMutability": "view" + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" }, { - "type": "function", - "name": "isMetadataFrozen", "inputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" } ], - "outputs": [ + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ { - "name": "", - "type": "bool", - "internalType": "bool" + "indexed": false, + "internalType": "address", + "name": "authority", + "type": "address" } ], - "stateMutability": "view" + "name": "AuthorityUpdated", + "type": "event" }, { - "type": "function", - "name": "name", - "inputs": [], - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", - "type": "string", - "internalType": "string" + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" } ], - "stateMutability": "pure" + "name": "Initialized", + "type": "event" }, { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ + "anonymous": false, + "inputs": [ { - "name": "", - "type": "bytes32", - "internalType": "bytes32" + "indexed": true, + "internalType": "address", + "name": "ipId", + "type": "address" } ], - "stateMutability": "view" + "name": "MetadataFrozen", + "type": "event" }, { - "type": "function", - "name": "setAll", + "anonymous": false, "inputs": [ { + "indexed": true, + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "indexed": false, + "internalType": "string", "name": "metadataURI", - "type": "string", - "internalType": "string" + "type": "string" }, { + "indexed": false, + "internalType": "bytes32", "name": "metadataHash", - "type": "bytes32", - "internalType": "bytes32" - }, - { - "name": "nftMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setAuthority", - "inputs": [ - { - "name": "newAuthority", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "MetadataURISet", + "type": "event" }, { - "type": "function", - "name": "setMetadataURI", + "anonymous": false, "inputs": [ { + "indexed": true, + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "metadataURI", - "type": "string", - "internalType": "string" + "indexed": false, + "internalType": "string", + "name": "nftTokenURI", + "type": "string" }, { - "name": "metadataHash", - "type": "bytes32", - "internalType": "bytes32" + "indexed": false, + "internalType": "bytes32", + "name": "nftMetadataHash", + "type": "bytes32" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "NFTTokenURISet", + "type": "event" }, { - "type": "function", - "name": "supportsInterface", + "anonymous": false, "inputs": [ { - "name": "interfaceId", - "type": "bytes4", - "internalType": "bytes4" + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" } ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "ACCESS_CONTROLLER", "outputs": [ { + "internalType": "contract IAccessController", "name": "", - "type": "bool", - "internalType": "bool" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "updateNftTokenURI", - "inputs": [ - { - "name": "ipId", - "type": "address", - "internalType": "address" - }, + "inputs": [], + "name": "IP_ASSET_REGISTRY", + "outputs": [ { - "name": "nftMetadataHash", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "contract IIPAssetRegistry", + "name": "", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ - { - "name": "newImplementation", - "type": "address", - "internalType": "address" - }, + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ { - "name": "data", - "type": "bytes", - "internalType": "bytes" + "internalType": "string", + "name": "", + "type": "string" } ], - "outputs": [], - "stateMutability": "payable" + "stateMutability": "view", + "type": "function" }, { - "type": "event", - "name": "AuthorityUpdated", - "inputs": [ + "inputs": [], + "name": "authority", + "outputs": [ { - "name": "authority", - "type": "address", - "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "", + "type": "address" } ], - "anonymous": false + "stateMutability": "view", + "type": "function" }, { - "type": "event", - "name": "Initialized", "inputs": [ { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" + "internalType": "address", + "name": "ipId", + "type": "address" } ], - "anonymous": false + "name": "freezeMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "event", - "name": "MetadataFrozen", "inputs": [ { - "name": "ipId", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "accessManager", + "type": "address" } ], - "anonymous": false + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "event", - "name": "MetadataURISet", - "inputs": [ - { - "name": "ipId", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "metadataURI", - "type": "string", - "indexed": false, - "internalType": "string" - }, + "inputs": [], + "name": "isConsumingScheduledOp", + "outputs": [ { - "name": "metadataHash", - "type": "bytes32", - "indexed": false, - "internalType": "bytes32" + "internalType": "bytes4", + "name": "", + "type": "bytes4" } ], - "anonymous": false + "stateMutability": "view", + "type": "function" }, { - "type": "event", - "name": "NFTTokenURISet", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "nftTokenURI", - "type": "string", - "indexed": false, - "internalType": "string" - }, - { - "name": "nftMetadataHash", - "type": "bytes32", - "indexed": false, - "internalType": "bytes32" + "type": "address" } ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ + "name": "isMetadataFrozen", + "outputs": [ { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "bool", + "name": "", + "type": "bool" } ], - "anonymous": false + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessControlled__NotIpAccount", - "inputs": [ + "inputs": [], + "name": "name", + "outputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" + "internalType": "string", + "name": "", + "type": "string" } - ] - }, - { - "type": "error", - "name": "AccessControlled__ZeroAddress", - "inputs": [] + ], + "stateMutability": "pure", + "type": "function" }, { - "type": "error", - "name": "AccessManagedInvalidAuthority", - "inputs": [ + "inputs": [], + "name": "proxiableUUID", + "outputs": [ { - "name": "authority", - "type": "address", - "internalType": "address" + "internalType": "bytes32", + "name": "", + "type": "bytes32" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessManagedRequiredDelay", "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" }, { - "name": "delay", - "type": "uint32", - "internalType": "uint32" + "internalType": "string", + "name": "metadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "metadataHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "nftMetadataHash", + "type": "bytes32" } - ] + ], + "name": "setAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AccessManagedUnauthorized", "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "newAuthority", + "type": "address" } - ] + ], + "name": "setAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AddressEmptyCode", "inputs": [ { - "name": "target", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "internalType": "string", + "name": "metadataURI", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "metadataHash", + "type": "bytes32" } - ] - }, - { - "type": "error", - "name": "CoreMetadataModule__MetadataAlreadyFrozen", - "inputs": [] + ], + "name": "setMetadataURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "CoreMetadataModule__MetadataURIContainsDoubleQuote", "inputs": [ { - "name": "uri", - "type": "string", - "internalType": "string" + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" } - ] - }, - { - "type": "error", - "name": "CoreMetadataModule__NFTTokenURIContainsDoubleQuote", - "inputs": [ + ], + "name": "supportsInterface", + "outputs": [ { - "name": "uri", - "type": "string", - "internalType": "string" + "internalType": "bool", + "name": "", + "type": "bool" } - ] - }, - { - "type": "error", - "name": "CoreMetadataModule__ZeroAccessManager", - "inputs": [] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "ERC1967InvalidImplementation", "inputs": [ { - "name": "implementation", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "nftMetadataHash", + "type": "bytes32" } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] + ], + "name": "updateNftTokenURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", "inputs": [ { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" } - ] + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/jsons/LicensingModule.json b/src/story_protocol_python_sdk/abi/jsons/LicensingModule.json index 8cb75d3..2742686 100644 --- a/src/story_protocol_python_sdk/abi/jsons/LicensingModule.json +++ b/src/story_protocol_python_sdk/abi/jsons/LicensingModule.json @@ -1,1253 +1,1357 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "accessController", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipAccountRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "moduleRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "royaltyModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licenseRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "disputeModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "licenseToken", - "type": "address", - "internalType": "address" + "type": "address" + }, + { + "internalType": "address", + "name": "ipGraphAcl", + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" }, { - "type": "function", - "name": "ACCESS_CONTROLLER", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IAccessController" + "internalType": "address", + "name": "ipAccount", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessControlled__NotIpAccount", + "type": "error" }, { - "type": "function", - "name": "DISPUTE_MODULE", "inputs": [], - "outputs": [ + "name": "AccessControlled__ZeroAddress", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IDisputeModule" + "internalType": "address", + "name": "authority", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedInvalidAuthority", + "type": "error" }, { - "type": "function", - "name": "IP_ASSET_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IIPAssetRegistry" + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" } ], - "stateMutability": "view" + "name": "AccessManagedRequiredDelay", + "type": "error" }, { - "type": "function", - "name": "LICENSE_NFT", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract ILicenseToken" + "internalType": "address", + "name": "caller", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedUnauthorized", + "type": "error" }, { - "type": "function", - "name": "LICENSE_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract ILicenseRegistry" + "internalType": "address", + "name": "target", + "type": "address" } ], - "stateMutability": "view" + "name": "AddressEmptyCode", + "type": "error" }, { - "type": "function", - "name": "MODULE_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IModuleRegistry" + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "stateMutability": "view" + "name": "ERC1967InvalidImplementation", + "type": "error" }, { - "type": "function", - "name": "ROYALTY_MODULE", "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract RoyaltyModule" - } - ], - "stateMutability": "view" + "name": "ERC1967NonPayable", + "type": "error" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" + "name": "EnforcedPause", + "type": "error" + }, + { + "inputs": [], + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "LicenseRegistry__LicenseTemplateCannotBeZeroAddress", + "type": "error" }, { - "type": "function", - "name": "__ProtocolPausable_init", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicenseRegistry__UnregisteredLicenseTemplate", + "type": "error" }, { - "type": "function", - "name": "attachLicenseTerms", "inputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" - }, - { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "authority", - "inputs": [], - "outputs": [ + "type": "uint256" + }, { - "name": "", - "type": "address", - "internalType": "address" + "internalType": "uint32", + "name": "newRoyaltyPercent", + "type": "uint32" } ], - "stateMutability": "view" + "name": "LicensingModule__CurrentLicenseNotAllowOverrideRoyaltyPercent", + "type": "error" }, { - "type": "function", - "name": "initialize", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "childIpId", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__DerivativeAlreadyHasBeenMintedLicenseTokens", + "type": "error" }, { - "type": "function", - "name": "isConsumingScheduledOp", "inputs": [], - "outputs": [ - { - "name": "", - "type": "bytes4", - "internalType": "bytes4" - } - ], - "stateMutability": "view" + "name": "LicensingModule__DisputedIpId", + "type": "error" }, { - "type": "function", - "name": "mintLicenseTokens", "inputs": [ { - "name": "licensorIpId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" }, { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "receiver", - "type": "address", - "internalType": "address" + "type": "uint256" }, { - "name": "royaltyContext", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "maxMintingFee", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint32", + "name": "revenueShare", + "type": "uint32" }, { + "internalType": "uint32", "name": "maxRevenueShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "outputs": [ + "name": "LicensingModule__ExceedMaxRevenueShare", + "type": "error" + }, + { + "inputs": [ { - "name": "startLicenseTokenId", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "nonpayable" + "name": "LicensingModule__GroupIpCannotChangeHookData", + "type": "error" }, { - "type": "function", - "name": "name", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "string", - "internalType": "string" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "view" + "name": "LicensingModule__GroupIpCannotChangeIsSet", + "type": "error" }, { - "type": "function", - "name": "pause", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" + "inputs": [ + { + "internalType": "address", + "name": "groupId", + "type": "address" + } + ], + "name": "LicensingModule__GroupIpCannotChangeLicensingHook", + "type": "error" }, { - "type": "function", - "name": "paused", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bool", - "internalType": "bool" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "stateMutability": "view" + "name": "LicensingModule__GroupIpCannotChangeMintingFee", + "type": "error" }, { - "type": "function", - "name": "predictMintingLicenseFee", "inputs": [ { - "name": "licensorIpId", - "type": "address", - "internalType": "address" - }, - { - "name": "licenseTemplate", - "type": "address", - "internalType": "address" - }, - { - "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "groupId", + "type": "address" }, { - "name": "amount", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint32", + "name": "newRoyaltyPercent", + "type": "uint32" }, { - "name": "receiver", - "type": "address", - "internalType": "address" - }, + "internalType": "uint32", + "name": "oldRoyaltyPercent", + "type": "uint32" + } + ], + "name": "LicensingModule__GroupIpCannotDecreaseRoyalty", + "type": "error" + }, + { + "inputs": [ { - "name": "royaltyContext", - "type": "bytes", - "internalType": "bytes" + "internalType": "address", + "name": "groupId", + "type": "address" } ], - "outputs": [ + "name": "LicensingModule__GroupIpCannotSetExpectGroupRewardPool", + "type": "error" + }, + { + "inputs": [ { - "name": "currencyToken", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "tokenAmount", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" } ], - "stateMutability": "view" + "name": "LicensingModule__InvalidLicenseTermsId", + "type": "error" }, { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "hook", + "type": "address" } ], - "stateMutability": "view" + "name": "LicensingModule__InvalidLicensingHook", + "type": "error" }, { - "type": "function", - "name": "registerDerivative", "inputs": [ { - "name": "childIpId", - "type": "address", - "internalType": "address" - }, - { - "name": "parentIpIds", - "type": "address[]", - "internalType": "address[]" - }, - { - "name": "licenseTermsIds", - "type": "uint256[]", - "internalType": "uint256[]" - }, - { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "royaltyContext", - "type": "bytes", - "internalType": "bytes" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" }, { - "name": "maxMintingFee", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "licensorIpId", + "type": "address" + } + ], + "name": "LicensingModule__LicenseDenyMintLicenseToken", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "ipId", + "type": "address" }, { - "name": "maxRts", - "type": "uint32", - "internalType": "uint32" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "maxRevenueShare", - "type": "uint32", - "internalType": "uint32" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__LicenseDisabled", + "type": "error" }, { - "type": "function", - "name": "registerDerivativeWithLicenseTokens", "inputs": [ { + "internalType": "address", "name": "childIpId", - "type": "address", - "internalType": "address" - }, - { - "name": "licenseTokenIds", - "type": "uint256[]", - "internalType": "uint256[]" - }, + "type": "address" + } + ], + "name": "LicensingModule__LicenseNotCompatibleForDerivative", + "type": "error" + }, + { + "inputs": [ { - "name": "royaltyContext", - "type": "bytes", - "internalType": "bytes" + "internalType": "uint256", + "name": "ipLength", + "type": "uint256" }, { - "name": "maxRts", - "type": "uint32", - "internalType": "uint32" + "internalType": "uint256", + "name": "licenseTermsLength", + "type": "uint256" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__LicenseTermsLengthMismatch", + "type": "error" }, { - "type": "function", - "name": "setAuthority", "inputs": [ { - "name": "newAuthority", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "childIpId", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "licenseTokenIds", + "type": "uint256[]" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__LicenseTokenNotCompatibleForDerivative", + "type": "error" }, { - "type": "function", - "name": "setLicensingConfig", "inputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" - }, - { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "licensingConfig", - "type": "tuple", - "internalType": "struct Licensing.LicensingConfig", - "components": [ - { - "name": "isSet", - "type": "bool", - "internalType": "bool" - }, - { - "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "licensingHook", - "type": "address", - "internalType": "address" - }, - { - "name": "hookData", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "commercialRevShare", - "type": "uint32", - "internalType": "uint32" - }, - { - "name": "disabled", - "type": "bool", - "internalType": "bool" - }, - { - "name": "expectMinimumGroupRewardShare", - "type": "uint32", - "internalType": "uint32" - }, - { - "name": "expectGroupRewardPool", - "type": "address", - "internalType": "address" - } - ] + "internalType": "uint256", + "name": "licensingConfigMintingFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "licenseTermsMintingFee", + "type": "uint256" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__LicensingConfigMintingFeeBelowLicenseTerms", + "type": "error" }, { - "type": "function", - "name": "supportsInterface", "inputs": [ { - "name": "interfaceId", - "type": "bytes4", - "internalType": "bytes4" + "internalType": "uint256", + "name": "licensingHookMintingFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "licenseTermsMintingFee", + "type": "uint256" } ], - "outputs": [ + "name": "LicensingModule__LicensingHookMintingFeeBelowLicenseTerms", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__LicensorIpNotRegistered", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__MintAmountZero", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "bool", - "internalType": "bool" + "internalType": "uint256", + "name": "mintingFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxMintingFee", + "type": "uint256" } ], - "stateMutability": "view" + "name": "LicensingModule__MintingFeeExceedMaxMintingFee", + "type": "error" }, { - "type": "function", - "name": "unpause", "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" + "name": "LicensingModule__MintingFeeRequiresRoyaltyPolicy", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__NoLicenseToken", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__NoParentIp", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ReceiverZeroAddress", + "type": "error" }, { - "type": "function", - "name": "upgradeToAndCall", "inputs": [ { - "name": "newImplementation", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "royaltyPolicy", + "type": "address" }, { - "name": "data", - "type": "bytes", - "internalType": "bytes" + "internalType": "address", + "name": "anotherRoyaltyPolicy", + "type": "address" } ], - "outputs": [], - "stateMutability": "payable" + "name": "LicensingModule__RoyaltyPolicyMismatch", + "type": "error" }, { - "type": "event", - "name": "AuthorityUpdated", "inputs": [ { - "name": "authority", - "type": "address", + "internalType": "address", + "name": "licensorIpId", + "type": "address" + }, + { + "internalType": "uint256", + "name": "ancestors", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxAncestors", + "type": "uint256" + } + ], + "name": "LicensingModule__TooManyAncestorsForMintingLicenseTokenAllowRegisterDerivative", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroDisputeModule", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroIPGraphACL", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroLicenseRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroLicenseTemplate", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroLicenseToken", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroModuleRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "LicensingModule__ZeroRoyaltyModule", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "authority", + "type": "address" } ], - "anonymous": false + "name": "AuthorityUpdated", + "type": "event" }, { - "type": "event", - "name": "DerivativeRegistered", + "anonymous": false, "inputs": [ { - "name": "caller", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" }, { - "name": "childIpId", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "childIpId", + "type": "address" }, { - "name": "licenseTokenIds", - "type": "uint256[]", "indexed": false, - "internalType": "uint256[]" + "internalType": "uint256[]", + "name": "licenseTokenIds", + "type": "uint256[]" }, { - "name": "parentIpIds", - "type": "address[]", "indexed": false, - "internalType": "address[]" + "internalType": "address[]", + "name": "parentIpIds", + "type": "address[]" }, { - "name": "licenseTermsIds", - "type": "uint256[]", "indexed": false, - "internalType": "uint256[]" + "internalType": "uint256[]", + "name": "licenseTermsIds", + "type": "uint256[]" }, { - "name": "licenseTemplate", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" } ], - "anonymous": false + "name": "DerivativeRegistered", + "type": "event" }, { - "type": "event", - "name": "Initialized", + "anonymous": false, "inputs": [ { - "name": "version", - "type": "uint64", "indexed": false, - "internalType": "uint64" + "internalType": "uint64", + "name": "version", + "type": "uint64" } ], - "anonymous": false + "name": "Initialized", + "type": "event" }, { - "type": "event", - "name": "LicenseTermsAttached", + "anonymous": false, "inputs": [ { - "name": "caller", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" }, { - "name": "ipId", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" }, { - "name": "licenseTemplate", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "licenseTermsId", - "type": "uint256", "indexed": false, - "internalType": "uint256" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" } ], - "anonymous": false + "name": "LicenseTermsAttached", + "type": "event" }, { - "type": "event", - "name": "LicenseTokensMinted", + "anonymous": false, "inputs": [ { - "name": "caller", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" }, { - "name": "licensorIpId", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "licensorIpId", + "type": "address" }, { - "name": "licenseTemplate", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "licenseTermsId", - "type": "uint256", "indexed": true, - "internalType": "uint256" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" }, { - "name": "amount", - "type": "uint256", "indexed": false, - "internalType": "uint256" + "internalType": "uint256", + "name": "amount", + "type": "uint256" }, { - "name": "receiver", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "receiver", + "type": "address" }, { - "name": "startLicenseTokenId", - "type": "uint256", "indexed": false, - "internalType": "uint256" + "internalType": "uint256", + "name": "startLicenseTokenId", + "type": "uint256" } ], - "anonymous": false + "name": "LicenseTokensMinted", + "type": "event" }, { - "type": "event", - "name": "Paused", + "anonymous": false, "inputs": [ { - "name": "account", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "account", + "type": "address" } ], - "anonymous": false + "name": "Paused", + "type": "event" }, { - "type": "event", - "name": "Unpaused", + "anonymous": false, "inputs": [ { - "name": "account", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "account", + "type": "address" } ], - "anonymous": false + "name": "Unpaused", + "type": "event" }, { - "type": "event", - "name": "Upgraded", + "anonymous": false, "inputs": [ { - "name": "implementation", - "type": "address", "indexed": true, - "internalType": "address" + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "anonymous": false + "name": "Upgraded", + "type": "event" }, { - "type": "error", - "name": "AccessControlled__NotIpAccount", - "inputs": [ + "inputs": [], + "name": "ACCESS_CONTROLLER", + "outputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" + "internalType": "contract IAccessController", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessControlled__ZeroAddress", - "inputs": [] + "inputs": [], + "name": "DISPUTE_MODULE", + "outputs": [ + { + "internalType": "contract IDisputeModule", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessManagedInvalidAuthority", - "inputs": [ + "inputs": [], + "name": "IP_ASSET_REGISTRY", + "outputs": [ { - "name": "authority", - "type": "address", - "internalType": "address" + "internalType": "contract IIPAssetRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessManagedRequiredDelay", - "inputs": [ - { - "name": "caller", - "type": "address", - "internalType": "address" - }, + "inputs": [], + "name": "IP_GRAPH_ACL", + "outputs": [ { - "name": "delay", - "type": "uint32", - "internalType": "uint32" + "internalType": "contract IPGraphACL", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessManagedUnauthorized", - "inputs": [ + "inputs": [], + "name": "LICENSE_NFT", + "outputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "contract ILicenseToken", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ + "inputs": [], + "name": "LICENSE_REGISTRY", + "outputs": [ { - "name": "target", - "type": "address", - "internalType": "address" + "internalType": "contract ILicenseRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ + "inputs": [], + "name": "MODULE_REGISTRY", + "outputs": [ { - "name": "implementation", - "type": "address", - "internalType": "address" + "internalType": "contract IModuleRegistry", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] + "inputs": [], + "name": "ROYALTY_MODULE", + "outputs": [ + { + "internalType": "contract RoyaltyModule", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicenseRegistry__LicenseTemplateCannotBeZeroAddress", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "accessManager", + "type": "address" + } + ], + "name": "__ProtocolPausable_init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicenseRegistry__UnregisteredLicenseTemplate", "inputs": [ { - "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipId", + "type": "address" } - ] + ], + "name": "attachDefaultLicenseTerms", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__CurrentLicenseNotAllowOverrideRoyaltyPercent", "inputs": [ { + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" - }, + "type": "uint256" + } + ], + "name": "attachLicenseTerms", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "authority", + "outputs": [ { - "name": "newRoyaltyPercent", - "type": "uint32", - "internalType": "uint32" + "internalType": "address", + "name": "", + "type": "address" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__DerivativeAlreadyHasBeenMintedLicenseTokens", "inputs": [ { - "name": "childIpId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "accessManager", + "type": "address" } - ] + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__DisputedIpId", - "inputs": [] + "inputs": [], + "name": "isConsumingScheduledOp", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__ExceedMaxRevenueShare", "inputs": [ { - "name": "ipId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "licensorIpId", + "type": "address" }, { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256", "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" }, { - "name": "revenueShare", - "type": "uint32", - "internalType": "uint32" + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" }, { + "internalType": "bytes", + "name": "royaltyContext", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxMintingFee", + "type": "uint256" + }, + { + "internalType": "uint32", "name": "maxRevenueShare", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } - ] - }, - { - "type": "error", - "name": "LicensingModule__GroupIpCannotChangeHookData", - "inputs": [ + ], + "name": "mintLicenseTokens", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "uint256", + "name": "startLicenseTokenId", + "type": "uint256" } - ] + ], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__GroupIpCannotChangeIsSet", - "inputs": [ + "inputs": [], + "name": "name", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "string", + "name": "", + "type": "string" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__GroupIpCannotChangeLicensingHook", - "inputs": [ - { - "name": "groupId", - "type": "address", - "internalType": "address" - } - ] + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__GroupIpCannotChangeMintingFee", - "inputs": [ + "inputs": [], + "name": "paused", + "outputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "bool", + "name": "", + "type": "bool" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__GroupIpCannotDecreaseRoyalty", "inputs": [ { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "licensorIpId", + "type": "address" }, { - "name": "newRoyaltyPercent", - "type": "uint32", - "internalType": "uint32" + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "oldRoyaltyPercent", - "type": "uint32", - "internalType": "uint32" - } - ] - }, - { - "type": "error", - "name": "LicensingModule__GroupIpCannotSetExpectGroupRewardPool", - "inputs": [ + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + }, { - "name": "groupId", - "type": "address", - "internalType": "address" + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bytes", + "name": "royaltyContext", + "type": "bytes" } - ] - }, - { - "type": "error", - "name": "LicensingModule__InvalidLicenseTermsId", - "inputs": [ + ], + "name": "predictMintingLicenseFee", + "outputs": [ { - "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "currencyToken", + "type": "address" }, { - "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__InvalidLicensingHook", - "inputs": [ + "inputs": [], + "name": "proxiableUUID", + "outputs": [ { - "name": "hook", - "type": "address", - "internalType": "address" + "internalType": "bytes32", + "name": "", + "type": "bytes32" } - ] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__LicenseDenyMintLicenseToken", "inputs": [ { - "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "childIpId", + "type": "address" }, { - "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" + "internalType": "address[]", + "name": "parentIpIds", + "type": "address[]" }, { - "name": "licensorIpId", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "LicensingModule__LicenseDisabled", - "inputs": [ - { - "name": "ipId", - "type": "address", - "internalType": "address" + "internalType": "uint256[]", + "name": "licenseTermsIds", + "type": "uint256[]" }, { + "internalType": "address", "name": "licenseTemplate", - "type": "address", - "internalType": "address" + "type": "address" }, { - "name": "licenseTermsId", - "type": "uint256", - "internalType": "uint256" - } - ] - }, - { - "type": "error", - "name": "LicensingModule__LicenseNotCompatibleForDerivative", - "inputs": [ + "internalType": "bytes", + "name": "royaltyContext", + "type": "bytes" + }, { - "name": "childIpId", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "LicensingModule__LicenseTemplateCannotBeZeroAddressToOverrideRoyaltyPercent", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__LicenseTermsLengthMismatch", - "inputs": [ + "internalType": "uint256", + "name": "maxMintingFee", + "type": "uint256" + }, { - "name": "ipLength", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint32", + "name": "maxRts", + "type": "uint32" }, { - "name": "licenseTermsLength", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint32", + "name": "maxRevenueShare", + "type": "uint32" } - ] + ], + "name": "registerDerivative", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__LicenseTokenNotCompatibleForDerivative", "inputs": [ { + "internalType": "address", "name": "childIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint256[]", "name": "licenseTokenIds", - "type": "uint256[]", - "internalType": "uint256[]" + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "royaltyContext", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "maxRts", + "type": "uint32" } - ] - }, - { - "type": "error", - "name": "LicensingModule__LicensorIpNotRegistered", - "inputs": [] + ], + "name": "registerDerivativeWithLicenseTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__MintAmountZero", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "newAuthority", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__MintingFeeExceedMaxMintingFee", "inputs": [ { - "name": "mintingFee", - "type": "uint256", - "internalType": "uint256" + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "internalType": "address", + "name": "licenseTemplate", + "type": "address" }, { - "name": "maxMintingFeeRemaining", - "type": "uint256", - "internalType": "uint256" + "internalType": "uint256", + "name": "licenseTermsId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isSet", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "mintingFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "licensingHook", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "commercialRevShare", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "disabled", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "expectMinimumGroupRewardShare", + "type": "uint32" + }, + { + "internalType": "address", + "name": "expectGroupRewardPool", + "type": "address" + } + ], + "internalType": "struct Licensing.LicensingConfig", + "name": "licensingConfig", + "type": "tuple" } - ] - }, - { - "type": "error", - "name": "LicensingModule__NoLicenseToken", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__NoParentIp", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ReceiverZeroAddress", - "inputs": [] + ], + "name": "setLicensingConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "LicensingModule__RoyaltyPolicyMismatch", "inputs": [ { - "name": "royaltyPolicy", - "type": "address", - "internalType": "address" - }, + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ { - "name": "anotherRoyaltyPolicy", - "type": "address", - "internalType": "address" + "internalType": "bool", + "name": "", + "type": "bool" } - ] - }, - { - "type": "error", - "name": "LicensingModule__ZeroAccessManager", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ZeroDisputeModule", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ZeroLicenseRegistry", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ZeroLicenseToken", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ZeroModuleRegistry", - "inputs": [] - }, - { - "type": "error", - "name": "LicensingModule__ZeroRoyaltyModule", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", "inputs": [ { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" } - ] + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file From 2b0c29c7710769f95102fd6dd94538476295c0a7 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Wed, 16 Apr 2025 12:50:16 +0900 Subject: [PATCH 14/26] Updating setLicensingConfig() func --- .../resources/Group.py | 4 +- tests/integration/test_integration_group.py | 618 +++++++++--------- 2 files changed, 311 insertions(+), 311 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 12ce6e9..695a520 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -301,7 +301,7 @@ def register_ip_and_attach_license_and_add_to_group( 'signer': self.grouping_workflows_client.contract.address, 'to': self.licensing_module_client.contract.address, 'permission': 1, # ALLOW - 'func': "setLicensingConfig(address,uint256,tuple)" + 'func': "setLicensingConfig(address,address,uint256,(bool,uint256,address,bytes,uint32,bool,uint32,address))" } ] ) @@ -329,7 +329,7 @@ def register_ip_and_attach_license_and_add_to_group( print(f" Signature: {self.web3.to_bytes(hexstr=sig_add_to_group['signature'])}") print(f"Transaction Options: {tx_options}") print("============================") - + response = build_and_send_transaction( self.web3, self.account, diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 8155734..4614417 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -168,150 +168,7 @@ # assert isinstance(response['ip_id'], str) # assert response['ip_id'].startswith("0x") -# class TestAdvancedGroupOperations: -# @pytest.fixture(scope="module") -# def nft_collection(self, story_client): -# tx_data = story_client.NFTClient.create_nft_collection( -# name="test-collection", -# symbol="TEST", -# max_supply=100, -# is_public_minting=True, -# mint_open=True, -# contract_uri="test-uri", -# mint_fee_recipient=account.address, -# ) -# return tx_data['nft_contract'] - -# @pytest.fixture(scope="module") -# def ip_with_license(self, story_client, nft_collection): -# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=nft_collection, -# terms=[{ -# 'terms': { -# 'transferable': True, -# 'royalty_policy': ROYALTY_POLICY_LRP, -# 'default_minting_fee': 0, -# 'expiration': 1000, -# 'commercial_use': True, -# 'commercial_attribution': False, -# 'commercializer_checker': ZERO_ADDRESS, -# 'commercializer_checker_data': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'commercial_rev_ceiling': 0, -# 'derivatives_allowed': True, -# 'derivatives_attribution': True, -# 'derivatives_approval': False, -# 'derivatives_reciprocal': True, -# 'derivative_rev_ceiling': 0, -# 'currency': MockERC20, -# 'uri': "test case" -# }, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# ) - -# return { -# 'ip_id': response['ip_id'], -# 'license_terms_id': response['license_terms_ids'][0] -# } - -# @pytest.fixture(scope="module") -# def group_id(self, story_client): -# response = story_client.Group.register_group( -# group_pool=EVEN_SPLIT_GROUP_POOL -# ) -# return response['group_id'] - -# def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): -# token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - -# response = story_client.Group.register_ip_and_attach_license_and_add_to_group( -# group_id=group_id, -# nft_contract=MockERC721, -# token_id=token_id, -# max_allowed_reward_share=5, -# license_data=[{ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# ) - -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 - -# assert 'ip_id' in response -# assert isinstance(response['ip_id'], str) -# assert response['ip_id'].startswith("0x") - -# # def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): -# # response = story_client.Group.register_group_and_attach_license_and_add_ips( -# # group_pool=EVEN_SPLIT_GROUP_POOL, -# # max_allowed_reward_share=5, -# # ip_ids=[ip_with_license['ip_id']], -# # license_data={ -# # 'license_terms_id': ip_with_license['license_terms_id'], -# # 'licensing_config': { -# # 'is_set': True, -# # 'minting_fee': 0, -# # 'hook_data': ZERO_ADDRESS, -# # 'licensing_hook': ZERO_ADDRESS, -# # 'commercial_rev_share': 0, -# # 'disabled': False, -# # 'expect_minimum_group_reward_share': 0, -# # 'expect_group_reward_pool': ZERO_ADDRESS -# # } -# # } -# # ) - -# # assert 'tx_hash' in response -# # assert isinstance(response['tx_hash'], str) -# # assert len(response['tx_hash']) > 0 - -# # assert 'group_id' in response -# # assert isinstance(response['group_id'], str) -# # assert response['group_id'].startswith("0x") - -# # def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): -# # with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): -# # story_client.Group.register_group_and_attach_license_and_add_ips( -# # group_pool=EVEN_SPLIT_GROUP_POOL, -# # max_allowed_reward_share=5, -# # ip_ids=[ZERO_ADDRESS], # Invalid IP address -# # license_data={ -# # 'license_terms_id': ip_with_license['license_terms_id'], -# # 'licensing_config': { -# # 'is_set': True, -# # 'minting_fee': 0, -# # 'hook_data': ZERO_ADDRESS, -# # 'licensing_hook': ZERO_ADDRESS, -# # 'commercial_rev_share': 0, -# # 'disabled': False, -# # 'expect_minimum_group_reward_share': 0, -# # 'expect_group_reward_pool': ZERO_ADDRESS -# # } -# # } -# # ) - -class TestCollectRoyaltyAndClaimReward: +class TestAdvancedGroupOperations: @pytest.fixture(scope="module") def nft_collection(self, story_client): tx_data = story_client.NFTClient.create_nft_collection( @@ -324,198 +181,341 @@ def nft_collection(self, story_client): mint_fee_recipient=account.address, ) return tx_data['nft_contract'] - - @pytest.fixture(scope="module") - def setup_royalty_collection(self, story_client, nft_collection): - # Create license terms data - license_terms_data = [{ - 'terms': { - 'commercial_attribution': True, - 'commercial_rev_ceiling': 10, - 'commercial_rev_share': 10, - 'commercial_use': True, - 'commercializer_checker': ZERO_ADDRESS, - 'commercializer_checker_data': ZERO_ADDRESS, - 'currency': MockERC20, - 'derivative_rev_ceiling': 0, - 'derivatives_allowed': True, - 'derivatives_approval': False, - 'derivatives_attribution': True, - 'derivatives_reciprocal': True, - 'expiration': 0, - 'default_minting_fee': 0, - 'royalty_policy': ROYALTY_POLICY_LRP, - 'transferable': True, - 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" - }, - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 10, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }] - # Create unique metadata for each IP - metadata_1 = { - 'ip_metadata_uri': "test-uri-1", - 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-1")), - 'nft_metadata_uri': "test-nft-uri-1", - 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-a")) - } - - metadata_2 = { - 'ip_metadata_uri': "test-uri-2", - 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-2")), - 'nft_metadata_uri': "test-nft-uri-2", - 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-2")) - } - # Create two IPs - result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( - spg_nft_contract=nft_collection, - terms=copy.deepcopy(license_terms_data), - ip_metadata=metadata_1 - ) - result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + @pytest.fixture(scope="module") + def ip_with_license(self, story_client, nft_collection): + response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( spg_nft_contract=nft_collection, - terms=copy.deepcopy(license_terms_data), - ip_metadata=metadata_2 + terms=[{ + 'terms': { + 'transferable': True, + 'royalty_policy': ROYALTY_POLICY_LRP, + 'default_minting_fee': 0, + 'expiration': 1000, + 'commercial_use': True, + 'commercial_attribution': False, + 'commercializer_checker': ZERO_ADDRESS, + 'commercializer_checker_data': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'commercial_rev_ceiling': 0, + 'derivatives_allowed': True, + 'derivatives_attribution': True, + 'derivatives_approval': False, + 'derivatives_reciprocal': True, + 'derivative_rev_ceiling': 0, + 'currency': MockERC20, + 'uri': "test case" + }, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }] ) - ip_ids = [result1['ip_id'], result2['ip_id']] - license_terms_id = result1['license_terms_ids'][0] + return { + 'ip_id': response['ip_id'], + 'license_terms_id': response['license_terms_ids'][0] + } + + @pytest.fixture(scope="module") + def group_id(self, story_client): + response = story_client.Group.register_group( + group_pool=EVEN_SPLIT_GROUP_POOL + ) + return response['group_id'] + + def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): + token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - # Register group and add IPs - result3 = story_client.Group.register_group_and_attach_license_and_add_ips( - group_pool=EVEN_SPLIT_GROUP_POOL, - max_allowed_reward_share=100, - ip_ids=ip_ids, - license_data={ - 'license_terms_id': license_terms_id, - 'license_template': PIL_LICENSE_TEMPLATE, + response = story_client.Group.register_ip_and_attach_license_and_add_to_group( + group_id=group_id, + nft_contract=MockERC721, + token_id=token_id, + max_allowed_reward_share=5, + license_data=[{ + 'license_terms_id': ip_with_license['license_terms_id'], 'licensing_config': { 'is_set': True, 'minting_fee': 0, 'hook_data': ZERO_ADDRESS, 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 10, + 'commercial_rev_share': 0, 'disabled': False, 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL } - } + }] ) - group_ip_id = result3['group_id'] + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 - # Create derivative IPs - Step 1: Mint and register - result4 = story_client.IPAsset.mint_and_register_ip( - spg_nft_contract=nft_collection, - ip_metadata={ - 'ip_metadata_uri': "test-derivative-uri-4", - 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-1")), - 'nft_metadata_uri': "test-derivative-nft-uri-4", - 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-1")) - } - ) - child_ip_id1 = result4['ip_id'] + assert 'ip_id' in response + assert isinstance(response['ip_id'], str) + assert response['ip_id'].startswith("0x") + + # def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): + # response = story_client.Group.register_group_and_attach_license_and_add_ips( + # group_pool=EVEN_SPLIT_GROUP_POOL, + # max_allowed_reward_share=5, + # ip_ids=[ip_with_license['ip_id']], + # license_data={ + # 'license_terms_id': ip_with_license['license_terms_id'], + # 'licensing_config': { + # 'is_set': True, + # 'minting_fee': 0, + # 'hook_data': ZERO_ADDRESS, + # 'licensing_hook': ZERO_ADDRESS, + # 'commercial_rev_share': 0, + # 'disabled': False, + # 'expect_minimum_group_reward_share': 0, + # 'expect_group_reward_pool': ZERO_ADDRESS + # } + # } + # ) + + # assert 'tx_hash' in response + # assert isinstance(response['tx_hash'], str) + # assert len(response['tx_hash']) > 0 + + # assert 'group_id' in response + # assert isinstance(response['group_id'], str) + # assert response['group_id'].startswith("0x") + + # def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): + # with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): + # story_client.Group.register_group_and_attach_license_and_add_ips( + # group_pool=EVEN_SPLIT_GROUP_POOL, + # max_allowed_reward_share=5, + # ip_ids=[ZERO_ADDRESS], # Invalid IP address + # license_data={ + # 'license_terms_id': ip_with_license['license_terms_id'], + # 'licensing_config': { + # 'is_set': True, + # 'minting_fee': 0, + # 'hook_data': ZERO_ADDRESS, + # 'licensing_hook': ZERO_ADDRESS, + # 'commercial_rev_share': 0, + # 'disabled': False, + # 'expect_minimum_group_reward_share': 0, + # 'expect_group_reward_pool': ZERO_ADDRESS + # } + # } + # ) - # Step 2: Register as derivative - story_client.IPAsset.register_derivative( - child_ip_id=child_ip_id1, - parent_ip_ids=[group_ip_id], - license_terms_ids=[license_terms_id], - max_minting_fee=0, - max_rts=10, - max_revenue_share=0 - ) +# class TestCollectRoyaltyAndClaimReward: +# @pytest.fixture(scope="module") +# def nft_collection(self, story_client): +# tx_data = story_client.NFTClient.create_nft_collection( +# name="test-collection", +# symbol="TEST", +# max_supply=100, +# is_public_minting=True, +# mint_open=True, +# contract_uri="test-uri", +# mint_fee_recipient=account.address, +# ) +# return tx_data['nft_contract'] + +# @pytest.fixture(scope="module") +# def setup_royalty_collection(self, story_client, nft_collection): +# # Create license terms data +# license_terms_data = [{ +# 'terms': { +# 'commercial_attribution': True, +# 'commercial_rev_ceiling': 10, +# 'commercial_rev_share': 10, +# 'commercial_use': True, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'currency': MockERC20, +# 'derivative_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_approval': False, +# 'derivatives_attribution': True, +# 'derivatives_reciprocal': True, +# 'expiration': 0, +# 'default_minting_fee': 0, +# 'royalty_policy': ROYALTY_POLICY_LRP, +# 'transferable': True, +# 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 10, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# # Create unique metadata for each IP +# metadata_1 = { +# 'ip_metadata_uri': "test-uri-1", +# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-1")), +# 'nft_metadata_uri': "test-nft-uri-1", +# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-a")) +# } + +# metadata_2 = { +# 'ip_metadata_uri': "test-uri-2", +# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-2")), +# 'nft_metadata_uri': "test-nft-uri-2", +# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-2")) +# } - # Create second derivative IP - Step 1: Mint and register - result5 = story_client.IPAsset.mint_and_register_ip( - spg_nft_contract=nft_collection, - ip_metadata={ - 'ip_metadata_uri': "test-derivative-uri-5", - 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-2")), - 'nft_metadata_uri': "test-derivative-nft-uri-5", - 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-2")) - } - ) - child_ip_id2 = result5['ip_id'] +# # Create two IPs +# result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=copy.deepcopy(license_terms_data), +# ip_metadata=metadata_1 +# ) +# result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=copy.deepcopy(license_terms_data), +# ip_metadata=metadata_2 +# ) + +# ip_ids = [result1['ip_id'], result2['ip_id']] +# license_terms_id = result1['license_terms_ids'][0] + +# # Register group and add IPs +# result3 = story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=100, +# ip_ids=ip_ids, +# license_data={ +# 'license_terms_id': license_terms_id, +# 'license_template': PIL_LICENSE_TEMPLATE, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 10, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) + +# group_ip_id = result3['group_id'] + +# # Create derivative IPs - Step 1: Mint and register +# result4 = story_client.IPAsset.mint_and_register_ip( +# spg_nft_contract=nft_collection, +# ip_metadata={ +# 'ip_metadata_uri': "test-derivative-uri-4", +# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-1")), +# 'nft_metadata_uri': "test-derivative-nft-uri-4", +# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-1")) +# } +# ) +# child_ip_id1 = result4['ip_id'] - # Step 2: Register as derivative - story_client.IPAsset.register_derivative( - child_ip_id=child_ip_id2, - parent_ip_ids=[group_ip_id], - license_terms_ids=[license_terms_id], - max_minting_fee=0, - max_rts=10, - max_revenue_share=0 - ) +# # Step 2: Register as derivative +# story_client.IPAsset.register_derivative( +# child_ip_id=child_ip_id1, +# parent_ip_ids=[group_ip_id], +# license_terms_ids=[license_terms_id], +# max_minting_fee=0, +# max_rts=10, +# max_revenue_share=0 +# ) + +# # Create second derivative IP - Step 1: Mint and register +# result5 = story_client.IPAsset.mint_and_register_ip( +# spg_nft_contract=nft_collection, +# ip_metadata={ +# 'ip_metadata_uri': "test-derivative-uri-5", +# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-2")), +# 'nft_metadata_uri': "test-derivative-nft-uri-5", +# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-2")) +# } +# ) +# child_ip_id2 = result5['ip_id'] + +# # Step 2: Register as derivative +# story_client.IPAsset.register_derivative( +# child_ip_id=child_ip_id2, +# parent_ip_ids=[group_ip_id], +# license_terms_ids=[license_terms_id], +# max_minting_fee=0, +# max_rts=10, +# max_revenue_share=0 +# ) - # Pay royalties from child IPs to group IP - story_client.Royalty.pay_royalty_on_behalf( - receiver_ip_id=child_ip_id1, - payer_ip_id=group_ip_id, - token=MockERC20, - amount=100 - ) +# # Pay royalties from child IPs to group IP +# story_client.Royalty.pay_royalty_on_behalf( +# receiver_ip_id=child_ip_id1, +# payer_ip_id=group_ip_id, +# token=MockERC20, +# amount=100 +# ) - story_client.Royalty.pay_royalty_on_behalf( - receiver_ip_id=child_ip_id2, - payer_ip_id=group_ip_id, - token=MockERC20, - amount=100 - ) +# story_client.Royalty.pay_royalty_on_behalf( +# receiver_ip_id=child_ip_id2, +# payer_ip_id=group_ip_id, +# token=MockERC20, +# amount=100 +# ) - print("claimable revenue is ", story_client.Royalty.claimable_revenue( - royalty_vault_ip_id=child_ip_id1, - claimer=account.address, - token=MockERC20 - )) +# print("claimable revenue is ", story_client.Royalty.claimable_revenue( +# royalty_vault_ip_id=child_ip_id1, +# claimer=account.address, +# token=MockERC20 +# )) - # Transfer to vault - story_client.Royalty.transfer_to_vault( - royalty_policy="LRP", - ip_id=child_ip_id1, - ancestor_ip_id=group_ip_id, - token=MockERC20 - ) +# # Transfer to vault +# story_client.Royalty.transfer_to_vault( +# royalty_policy="LRP", +# ip_id=child_ip_id1, +# ancestor_ip_id=group_ip_id, +# token=MockERC20 +# ) - story_client.Royalty.transfer_to_vault( - royalty_policy="LRP", - ip_id=child_ip_id2, - ancestor_ip_id=group_ip_id, - token=MockERC20 - ) +# story_client.Royalty.transfer_to_vault( +# royalty_policy="LRP", +# ip_id=child_ip_id2, +# ancestor_ip_id=group_ip_id, +# token=MockERC20 +# ) - return { - 'group_ip_id': group_ip_id, - 'ip_ids': ip_ids - } +# return { +# 'group_ip_id': group_ip_id, +# 'ip_ids': ip_ids +# } - def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): - group_ip_id = setup_royalty_collection['group_ip_id'] - ip_ids = setup_royalty_collection['ip_ids'] +# def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): +# group_ip_id = setup_royalty_collection['group_ip_id'] +# ip_ids = setup_royalty_collection['ip_ids'] - response = story_client.Group.collect_and_distribute_group_royalties( - group_ip_id=group_ip_id, - currency_tokens=[MockERC20], - member_ip_ids=ip_ids - ) +# response = story_client.Group.collect_and_distribute_group_royalties( +# group_ip_id=group_ip_id, +# currency_tokens=[MockERC20], +# member_ip_ids=ip_ids +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 - assert 'collected_royalties' in response - assert len(response['collected_royalties']) > 0 - assert response['collected_royalties'][0]['amount'] == 20 +# assert 'collected_royalties' in response +# assert len(response['collected_royalties']) > 0 +# assert response['collected_royalties'][0]['amount'] == 20 - assert 'royalties_distributed' in response - assert len(response['royalties_distributed']) == 2 - assert response['royalties_distributed'][0]['amount'] == 10 - assert response['royalties_distributed'][1]['amount'] == 10 +# assert 'royalties_distributed' in response +# assert len(response['royalties_distributed']) == 2 +# assert response['royalties_distributed'][0]['amount'] == 10 +# assert response['royalties_distributed'][1]['amount'] == 10 From 0cf2fde02f0e82ec690096565adfe7ac4b935b7a Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 17 Apr 2025 14:06:20 +0900 Subject: [PATCH 15/26] Updated get_permission_signature for multiple permissions --- .../abi/jsons/AccessController.json | 1021 ++++++++++------- src/story_protocol_python_sdk/utils/sign.py | 33 +- 2 files changed, 642 insertions(+), 412 deletions(-) diff --git a/src/story_protocol_python_sdk/abi/jsons/AccessController.json b/src/story_protocol_python_sdk/abi/jsons/AccessController.json index 5c181d9..72ebaac 100644 --- a/src/story_protocol_python_sdk/abi/jsons/AccessController.json +++ b/src/story_protocol_python_sdk/abi/jsons/AccessController.json @@ -1,645 +1,868 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "ipAccountRegistry", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "moduleRegistry", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" }, { - "type": "function", - "name": "IP_ASSET_REGISTRY", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "address", - "internalType": "contract IIPAssetRegistry" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "MODULE_REGISTRY", - "inputs": [], - "outputs": [ + "internalType": "address", + "name": "signer", + "type": "address" + }, { - "name": "", - "type": "address", - "internalType": "contract IModuleRegistry" + "internalType": "address", + "name": "to", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessController__BothCallerAndRecipientAreNotRegisteredModule", + "type": "error" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" + "name": "AccessController__CallerIsNotIPAccountOrOwner", + "type": "error" }, { - "type": "function", - "name": "__ProtocolPausable_init", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipAccount", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "AccessController__IPAccountIsNotValid", + "type": "error" }, { - "type": "function", - "name": "authority", "inputs": [], - "outputs": [ + "name": "AccessController__IPAccountIsZeroAddress", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessController__OwnerIsIPAccount", + "type": "error" }, { - "type": "function", - "name": "checkPermission", "inputs": [ { + "internalType": "address", "name": "ipAccount", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "to", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes4", "name": "func", - "type": "bytes4", - "internalType": "bytes4" + "type": "bytes4" } ], - "outputs": [], - "stateMutability": "view" + "name": "AccessController__PermissionDenied", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__PermissionIsNotValid", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__SignerIsZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__ToAndFuncAreZeroAddressShouldCallSetAllPermissions", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__ZeroIPAccountRegistry", + "type": "error" + }, + { + "inputs": [], + "name": "AccessController__ZeroModuleRegistry", + "type": "error" }, { - "type": "function", - "name": "getPermission", "inputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" - }, - { - "name": "signer", - "type": "address", - "internalType": "address" - }, + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AccessManagedInvalidAuthority", + "type": "error" + }, + { + "inputs": [ { - "name": "to", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "caller", + "type": "address" }, { - "name": "func", - "type": "bytes4", - "internalType": "bytes4" + "internalType": "uint32", + "name": "delay", + "type": "uint32" } ], - "outputs": [ + "name": "AccessManagedRequiredDelay", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "uint8", - "internalType": "uint8" + "internalType": "address", + "name": "caller", + "type": "address" } ], - "stateMutability": "view" + "name": "AccessManagedUnauthorized", + "type": "error" }, { - "type": "function", - "name": "initialize", "inputs": [ { - "name": "accessManager", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "target", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "AddressEmptyCode", + "type": "error" }, { - "type": "function", - "name": "isConsumingScheduledOp", - "inputs": [], - "outputs": [ + "inputs": [ { - "name": "", - "type": "bytes4", - "internalType": "bytes4" + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "stateMutability": "view" + "name": "ERC1967InvalidImplementation", + "type": "error" }, { - "type": "function", - "name": "pause", "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" + "name": "ERC1967NonPayable", + "type": "error" }, { - "type": "function", - "name": "paused", "inputs": [], - "outputs": [ - { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" + "name": "EnforcedPause", + "type": "error" }, { - "type": "function", - "name": "proxiableUUID", "inputs": [], - "outputs": [ + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ { - "name": "", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" } ], - "stateMutability": "view" + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" }, { - "type": "function", - "name": "setAllPermissions", + "anonymous": false, "inputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" - }, - { - "name": "signer", - "type": "address", - "internalType": "address" - }, - { - "name": "permission", - "type": "uint8", - "internalType": "uint8" + "indexed": false, + "internalType": "address", + "name": "authority", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "AuthorityUpdated", + "type": "event" }, { - "type": "function", - "name": "setAuthority", + "anonymous": false, "inputs": [ { - "name": "newAuthority", - "type": "address", - "internalType": "address" + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "Initialized", + "type": "event" }, { - "type": "function", - "name": "setTransientBatchPermissions", + "anonymous": false, "inputs": [ { - "name": "permissions", - "type": "tuple[]", - "internalType": "struct AccessPermission.Permission[]", - "components": [ - { - "name": "ipAccount", - "type": "address", - "internalType": "address" - }, - { - "name": "signer", - "type": "address", - "internalType": "address" - }, - { - "name": "to", - "type": "address", - "internalType": "address" - }, - { - "name": "func", - "type": "bytes4", - "internalType": "bytes4" - }, - { - "name": "permission", - "type": "uint8", - "internalType": "uint8" - } - ] + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" } ], - "outputs": [], - "stateMutability": "nonpayable" + "name": "Paused", + "type": "event" }, { - "type": "function", - "name": "setTransientPermission", + "anonymous": false, "inputs": [ { + "indexed": false, + "internalType": "address", + "name": "ipAccountOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", "name": "ipAccount", - "type": "address", - "internalType": "address" + "type": "address" }, { + "indexed": true, + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "indexed": true, + "internalType": "address", "name": "to", - "type": "address", - "internalType": "address" + "type": "address" }, { + "indexed": false, + "internalType": "bytes4", "name": "func", - "type": "bytes4", - "internalType": "bytes4" + "type": "bytes4" }, { + "indexed": false, + "internalType": "uint8", "name": "permission", - "type": "uint8", - "internalType": "uint8" + "type": "uint8" } ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "unpause", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" + "name": "PermissionSet", + "type": "event" }, { - "type": "function", - "name": "upgradeToAndCall", + "anonymous": false, "inputs": [ { - "name": "newImplementation", - "type": "address", - "internalType": "address" + "indexed": false, + "internalType": "address", + "name": "ipAccountOwner", + "type": "address" }, { - "name": "data", - "type": "bytes", - "internalType": "bytes" + "indexed": true, + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "func", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "permission", + "type": "uint8" } ], - "outputs": [], - "stateMutability": "payable" + "name": "TransientPermissionSet", + "type": "event" }, { - "type": "event", - "name": "AuthorityUpdated", + "anonymous": false, "inputs": [ { - "name": "authority", - "type": "address", "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "account", + "type": "address" } ], - "anonymous": false + "name": "Unpaused", + "type": "event" }, { - "type": "event", - "name": "Initialized", + "anonymous": false, "inputs": [ { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "anonymous": false + "name": "Upgraded", + "type": "event" }, { - "type": "event", - "name": "Paused", - "inputs": [ + "inputs": [], + "name": "IP_ASSET_REGISTRY", + "outputs": [ { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" + "internalType": "contract IIPAssetRegistry", + "name": "", + "type": "address" } ], - "anonymous": false + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MODULE_REGISTRY", + "outputs": [ + { + "internalType": "contract IModuleRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "event", - "name": "PermissionSet", "inputs": [ { - "name": "ipAccountOwner", - "type": "address", - "indexed": false, - "internalType": "address" - }, + "internalType": "address", + "name": "accessManager", + "type": "address" + } + ], + "name": "__ProtocolPausable_init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "authority", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { + "internalType": "address", "name": "ipAccount", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "signer", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "to", - "type": "address", - "indexed": true, - "internalType": "address" + "type": "address" }, { + "internalType": "bytes4", "name": "func", - "type": "bytes4", - "indexed": false, - "internalType": "bytes4" - }, - { - "name": "permission", - "type": "uint8", - "indexed": false, - "internalType": "uint8" + "type": "bytes4" } ], - "anonymous": false + "name": "checkPermission", + "outputs": [], + "stateMutability": "view", + "type": "function" }, { - "type": "event", - "name": "Unpaused", "inputs": [ { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" } ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ + "name": "getPermanentPermission", + "outputs": [ { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" + "internalType": "uint8", + "name": "", + "type": "uint8" } ], - "anonymous": false + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessController__BothCallerAndRecipientAreNotRegisteredModule", "inputs": [ { + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "to", - "type": "address", - "internalType": "address" + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" } - ] - }, - { - "type": "error", - "name": "AccessController__CallerIsNotIPAccountOrOwner", - "inputs": [] - }, - { - "type": "error", - "name": "AccessController__IPAccountIsNotValid", - "inputs": [ + ], + "name": "getPermission", + "outputs": [ { - "name": "ipAccount", - "type": "address", - "internalType": "address" + "internalType": "uint8", + "name": "", + "type": "uint8" } - ] - }, - { - "type": "error", - "name": "AccessController__IPAccountIsZeroAddress", - "inputs": [] + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessController__PermissionDenied", "inputs": [ { + "internalType": "address", "name": "ipAccount", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "signer", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "to", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes4", "name": "func", - "type": "bytes4", - "internalType": "bytes4" + "type": "bytes4" } - ] - }, - { - "type": "error", - "name": "AccessController__PermissionIsNotValid", - "inputs": [] + ], + "name": "getTransientPermission", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessController__SignerIsZeroAddress", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "accessManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AccessController__ToAndFuncAreZeroAddressShouldCallSetAllPermissions", - "inputs": [] + "inputs": [], + "name": "isConsumingScheduledOp", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessController__ZeroAccessManager", - "inputs": [] + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AccessController__ZeroIPAccountRegistry", - "inputs": [] + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessController__ZeroModuleRegistry", - "inputs": [] + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" }, { - "type": "error", - "name": "AccessManagedInvalidAuthority", "inputs": [ { - "name": "authority", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" } - ] + ], + "name": "setAllPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AccessManagedRequiredDelay", "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "ipAccount", + "type": "address" }, { - "name": "delay", - "type": "uint32", - "internalType": "uint32" + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" } - ] + ], + "name": "setAllTransientPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AccessManagedUnauthorized", "inputs": [ { - "name": "caller", - "type": "address", - "internalType": "address" + "internalType": "address", + "name": "newAuthority", + "type": "address" } - ] + ], + "name": "setAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "AddressEmptyCode", "inputs": [ { - "name": "target", - "type": "address", - "internalType": "address" + "components": [ + { + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" + } + ], + "internalType": "struct AccessPermission.Permission[]", + "name": "permissions", + "type": "tuple[]" } - ] + ], + "name": "setBatchPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "ERC1967InvalidImplementation", "inputs": [ { - "name": "implementation", - "type": "address", - "internalType": "address" + "components": [ + { + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" + } + ], + "internalType": "struct AccessPermission.Permission[]", + "name": "permissions", + "type": "tuple[]" } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] + ], + "name": "setBatchTransientPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "NotInitializing", - "inputs": [] + "inputs": [ + { + "internalType": "address", + "name": "ipAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "func", + "type": "bytes4" + }, + { + "internalType": "uint8", + "name": "permission", + "type": "uint8" + } + ], + "name": "setTransientPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", "inputs": [ { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" } - ] + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file diff --git a/src/story_protocol_python_sdk/utils/sign.py b/src/story_protocol_python_sdk/utils/sign.py index e3018cd..f4ec5dd 100644 --- a/src/story_protocol_python_sdk/utils/sign.py +++ b/src/story_protocol_python_sdk/utils/sign.py @@ -104,20 +104,23 @@ def get_deadline(self, deadline: int = None) -> int: else: return current_timestamp + 1000 - def get_permission_signature(self, ip_id: str, deadline: int, permissions: list, permission_func: str = "setTransientPermission", state: str = None) -> dict: + def get_permission_signature(self, ip_id: str, deadline: int, permissions: list, permission_func: str = None, state: str = None) -> dict: """ Get the signature for setting permissions. :param ip_id str: The IP ID :param deadline int: The deadline :param permissions list: The permissions - :param permission_func str: The permission function (defaults to "setTransientPermission") + :param permission_func str: The permission function (defaults to None, which will auto-select based on permissions count) :param state str: The state :return dict: The signature response """ try: - # Get permission function name - permission_function = permission_func if permission_func else "setTransientPermission" + # Auto-select permission function based on number of permissions if not specified + if permission_func is None: + permission_function = "setBatchTransientPermissions" if len(permissions) >= 2 else "setTransientPermission" + else: + permission_function = permission_func # Get access controller address for chain access_address = self.access_controller_client.contract.address @@ -135,15 +138,19 @@ def get_permission_signature(self, ip_id: str, deadline: int, permissions: list, ] ) else: - # Encode multiple permissions - formatted_permissions = [{ - 'ipAccount': self.web3.to_checksum_address(p['ipId']), - 'signer': self.web3.to_checksum_address(p['signer']), - 'to': self.web3.to_checksum_address(p['to']), - 'func': Web3.keccak(text=p['func'])[:4].hex() if p.get('func') else ZERO_FUNC, - 'permission': p['permission'] - } for p in permissions] - + # Encode multiple permissions - format them correctly for the contract + formatted_permissions = [] + for p in permissions: + formatted_permission = { + 'ipAccount': self.web3.to_checksum_address(p['ipId']), + 'signer': self.web3.to_checksum_address(p['signer']), + 'to': self.web3.to_checksum_address(p['to']), + 'func': Web3.keccak(text=p['func'])[:4] if p.get('func') else b'\x00\x00\x00\x00', + 'permission': p['permission'] + } + formatted_permissions.append(formatted_permission) + + # Pass the array as a single argument encode_data = self.access_controller_client.contract.encode_abi( abi_element_identifier=permission_function, args=[formatted_permissions] From 6474b0104d862915d29c92a3f3ee170c0f585707 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 17 Apr 2025 14:08:13 +0900 Subject: [PATCH 16/26] Added register_ip_and_attach_license_and_add_to_group() to group module --- .../resources/Group.py | 18 -- tests/integration/test_integration_group.py | 291 +++++++++--------- 2 files changed, 153 insertions(+), 156 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 695a520..079d894 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -311,24 +311,6 @@ def register_ip_and_attach_license_and_add_to_group( # Process IP metadata metadata = self._get_ip_metadata(ip_metadata) - # Print all parameters before sending the transaction - print("=== Transaction Parameters ===") - print(f"NFT Contract: {nft_contract}") - print(f"Token ID: {token_id}") - print(f"Group ID: {group_id}") - print(f"Max Allowed Reward Share: {max_allowed_reward_share}") - print(f"Licenses Data: {licenses_data}") - print(f"Metadata: {metadata}") - print("Signature Metadata and Attach:") - print(f" Signer: {self.account.address}") - print(f" Deadline: {calculated_deadline}") - print(f" Signature: {self.web3.to_bytes(hexstr=sig_metadata_and_attach['signature'])}") - print("Signature Add to Group:") - print(f" Signer: {self.account.address}") - print(f" Deadline: {calculated_deadline}") - print(f" Signature: {self.web3.to_bytes(hexstr=sig_add_to_group['signature'])}") - print(f"Transaction Options: {tx_options}") - print("============================") response = build_and_send_transaction( self.web3, diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 4614417..1b14dcd 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -17,156 +17,156 @@ ROYALTY_POLICY_LRP ) -# class TestGroupBasicOperations: -# def test_register_basic_group(self, story_client): -# response = story_client.Group.register_group( -# group_pool=EVEN_SPLIT_GROUP_POOL -# ) +class TestGroupBasicOperations: + def test_register_basic_group(self, story_client): + response = story_client.Group.register_group( + group_pool=EVEN_SPLIT_GROUP_POOL + ) -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 -# assert 'group_id' in response -# assert isinstance(response['group_id'], str) -# assert response['group_id'].startswith("0x") + assert 'group_id' in response + assert isinstance(response['group_id'], str) + assert response['group_id'].startswith("0x") -# class TestGroupWithLicenseOperations: -# @pytest.fixture(scope="module") -# def nft_collection(self, story_client): -# tx_data = story_client.NFTClient.create_nft_collection( -# name="test-collection", -# symbol="TEST", -# max_supply=100, -# is_public_minting=True, -# mint_open=True, -# contract_uri="test-uri", -# mint_fee_recipient=account.address, -# ) -# return tx_data['nft_contract'] +class TestGroupWithLicenseOperations: + @pytest.fixture(scope="module") + def nft_collection(self, story_client): + tx_data = story_client.NFTClient.create_nft_collection( + name="test-collection", + symbol="TEST", + max_supply=100, + is_public_minting=True, + mint_open=True, + contract_uri="test-uri", + mint_fee_recipient=account.address, + ) + return tx_data['nft_contract'] -# @pytest.fixture(scope="module") -# def ip_with_license(self, story_client, nft_collection): -# # Create initial IP with license terms -# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=nft_collection, -# terms=[{ -# 'terms': { -# 'transferable': True, -# 'royalty_policy': ROYALTY_POLICY_LRP, -# 'default_minting_fee': 0, -# 'expiration': 1000, -# 'commercial_use': True, -# 'commercial_attribution': False, -# 'commercializer_checker': ZERO_ADDRESS, -# 'commercializer_checker_data': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'commercial_rev_ceiling': 0, -# 'derivatives_allowed': True, -# 'derivatives_attribution': True, -# 'derivatives_approval': False, -# 'derivatives_reciprocal': True, -# 'derivative_rev_ceiling': 0, -# 'currency': MockERC20, -# 'uri': "test case" -# }, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# ) + @pytest.fixture(scope="module") + def ip_with_license(self, story_client, nft_collection): + # Create initial IP with license terms + response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=[{ + 'terms': { + 'transferable': True, + 'royalty_policy': ROYALTY_POLICY_LRP, + 'default_minting_fee': 0, + 'expiration': 1000, + 'commercial_use': True, + 'commercial_attribution': False, + 'commercializer_checker': ZERO_ADDRESS, + 'commercializer_checker_data': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'commercial_rev_ceiling': 0, + 'derivatives_allowed': True, + 'derivatives_attribution': True, + 'derivatives_approval': False, + 'derivatives_reciprocal': True, + 'derivative_rev_ceiling': 0, + 'currency': MockERC20, + 'uri': "test case" + }, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }] + ) -# ip_id = response['ip_id'] -# license_terms_id = response['license_terms_ids'][0] + ip_id = response['ip_id'] + license_terms_id = response['license_terms_ids'][0] -# licensing_config = { -# 'isSet': True, -# 'mintingFee': 0, -# 'licensingHook': ZERO_ADDRESS, -# 'hookData': ZERO_ADDRESS, -# 'commercialRevShare': 0, -# 'disabled': False, -# 'expectMinimumGroupRewardShare': 0, -# 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL -# } + licensing_config = { + 'isSet': True, + 'mintingFee': 0, + 'licensingHook': ZERO_ADDRESS, + 'hookData': ZERO_ADDRESS, + 'commercialRevShare': 0, + 'disabled': False, + 'expectMinimumGroupRewardShare': 0, + 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL + } -# # Set licensing config -# story_client.License.set_licensing_config( -# ip_id=ip_id, -# license_terms_id=license_terms_id, -# license_template=PIL_LICENSE_TEMPLATE, -# licensing_config=licensing_config -# ) + # Set licensing config + story_client.License.set_licensing_config( + ip_id=ip_id, + license_terms_id=license_terms_id, + license_template=PIL_LICENSE_TEMPLATE, + licensing_config=licensing_config + ) -# return { -# 'ip_id': ip_id, -# 'license_terms_id': license_terms_id -# } + return { + 'ip_id': ip_id, + 'license_terms_id': license_terms_id + } -# @pytest.fixture(scope="module") -# def group_with_license(self, story_client, ip_with_license): -# response = story_client.Group.register_group_and_attach_license( -# group_pool=EVEN_SPLIT_GROUP_POOL, -# license_data={ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': ZERO_ADDRESS -# } -# } -# ) + @pytest.fixture(scope="module") + def group_with_license(self, story_client, ip_with_license): + response = story_client.Group.register_group_and_attach_license( + group_pool=EVEN_SPLIT_GROUP_POOL, + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) -# assert response is not None -# assert 'group_id' in response -# assert response['group_id'] is not None -# return response['group_id'] + assert response is not None + assert 'group_id' in response + assert response['group_id'] is not None + return response['group_id'] -# def test_register_group_and_attach_license(self, group_with_license): -# assert group_with_license is not None + def test_register_group_and_attach_license(self, group_with_license): + assert group_with_license is not None -# def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): -# response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( -# group_id=group_with_license, -# spg_nft_contract=nft_collection, -# license_data=[{ -# 'license_terms_id': ip_with_license['license_terms_id'], -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 0, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }], -# max_allowed_reward_share=5 -# ) + def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): + response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( + group_id=group_with_license, + spg_nft_contract=nft_collection, + license_data=[{ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }], + max_allowed_reward_share=5 + ) -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 -# assert 'ip_id' in response -# assert isinstance(response['ip_id'], str) -# assert response['ip_id'].startswith("0x") + assert 'ip_id' in response + assert isinstance(response['ip_id'], str) + assert response['ip_id'].startswith("0x") class TestAdvancedGroupOperations: @pytest.fixture(scope="module") @@ -225,9 +225,24 @@ def ip_with_license(self, story_client, nft_collection): } @pytest.fixture(scope="module") - def group_id(self, story_client): - response = story_client.Group.register_group( - group_pool=EVEN_SPLIT_GROUP_POOL + def group_id(self, story_client, ip_with_license): + # Create a group with license attached + response = story_client.Group.register_group_and_attach_license( + group_pool=EVEN_SPLIT_GROUP_POOL, + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'license_template': PIL_LICENSE_TEMPLATE, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } ) return response['group_id'] From 571d6f71b640324e014dd960c93e0dccb476a1bf Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 17 Apr 2025 14:28:45 +0900 Subject: [PATCH 17/26] collect_and_distribute_group_royalties() bug --- tests/integration/test_integration_group.py | 374 ++++++++++---------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 1b14dcd..5a2fc97 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -326,211 +326,211 @@ def test_register_ip_and_attach_license_and_add_to_group(self, story_client, gro # } # ) -# class TestCollectRoyaltyAndClaimReward: -# @pytest.fixture(scope="module") -# def nft_collection(self, story_client): -# tx_data = story_client.NFTClient.create_nft_collection( -# name="test-collection", -# symbol="TEST", -# max_supply=100, -# is_public_minting=True, -# mint_open=True, -# contract_uri="test-uri", -# mint_fee_recipient=account.address, -# ) -# return tx_data['nft_contract'] +class TestCollectRoyaltyAndClaimReward: + @pytest.fixture(scope="module") + def nft_collection(self, story_client): + tx_data = story_client.NFTClient.create_nft_collection( + name="test-collection", + symbol="TEST", + max_supply=100, + is_public_minting=True, + mint_open=True, + contract_uri="test-uri", + mint_fee_recipient=account.address, + ) + return tx_data['nft_contract'] -# @pytest.fixture(scope="module") -# def setup_royalty_collection(self, story_client, nft_collection): -# # Create license terms data -# license_terms_data = [{ -# 'terms': { -# 'commercial_attribution': True, -# 'commercial_rev_ceiling': 10, -# 'commercial_rev_share': 10, -# 'commercial_use': True, -# 'commercializer_checker': ZERO_ADDRESS, -# 'commercializer_checker_data': ZERO_ADDRESS, -# 'currency': MockERC20, -# 'derivative_rev_ceiling': 0, -# 'derivatives_allowed': True, -# 'derivatives_approval': False, -# 'derivatives_attribution': True, -# 'derivatives_reciprocal': True, -# 'expiration': 0, -# 'default_minting_fee': 0, -# 'royalty_policy': ROYALTY_POLICY_LRP, -# 'transferable': True, -# 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" -# }, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 10, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL -# } -# }] -# # Create unique metadata for each IP -# metadata_1 = { -# 'ip_metadata_uri': "test-uri-1", -# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-1")), -# 'nft_metadata_uri': "test-nft-uri-1", -# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-a")) -# } + @pytest.fixture(scope="module") + def setup_royalty_collection(self, story_client, nft_collection): + # Create license terms data + license_terms_data = [{ + 'terms': { + 'commercial_attribution': True, + 'commercial_rev_ceiling': 10, + 'commercial_rev_share': 10, + 'commercial_use': True, + 'commercializer_checker': ZERO_ADDRESS, + 'commercializer_checker_data': ZERO_ADDRESS, + 'currency': MockERC20, + 'derivative_rev_ceiling': 0, + 'derivatives_allowed': True, + 'derivatives_approval': False, + 'derivatives_attribution': True, + 'derivatives_reciprocal': True, + 'expiration': 0, + 'default_minting_fee': 0, + 'royalty_policy': ROYALTY_POLICY_LRP, + 'transferable': True, + 'uri': "https://github.com/piplabs/pil-document/blob/ad67bb632a310d2557f8abcccd428e4c9c798db1/off-chain-terms/CommercialRemix.json" + }, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 10, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL + } + }] + # Create unique metadata for each IP + metadata_1 = { + 'ip_metadata_uri': "test-uri-1", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-1")), + 'nft_metadata_uri': "test-nft-uri-1", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-a")) + } -# metadata_2 = { -# 'ip_metadata_uri': "test-uri-2", -# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-2")), -# 'nft_metadata_uri': "test-nft-uri-2", -# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-2")) -# } + metadata_2 = { + 'ip_metadata_uri': "test-uri-2", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-metadata-hash-2")), + 'nft_metadata_uri': "test-nft-uri-2", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-nft-metadata-hash-2")) + } -# # Create two IPs -# result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=nft_collection, -# terms=copy.deepcopy(license_terms_data), -# ip_metadata=metadata_1 -# ) -# result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( -# spg_nft_contract=nft_collection, -# terms=copy.deepcopy(license_terms_data), -# ip_metadata=metadata_2 -# ) + # Create two IPs + result1 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=copy.deepcopy(license_terms_data), + ip_metadata=metadata_1 + ) + result2 = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( + spg_nft_contract=nft_collection, + terms=copy.deepcopy(license_terms_data), + ip_metadata=metadata_2 + ) -# ip_ids = [result1['ip_id'], result2['ip_id']] -# license_terms_id = result1['license_terms_ids'][0] + ip_ids = [result1['ip_id'], result2['ip_id']] + license_terms_id = result1['license_terms_ids'][0] -# # Register group and add IPs -# result3 = story_client.Group.register_group_and_attach_license_and_add_ips( -# group_pool=EVEN_SPLIT_GROUP_POOL, -# max_allowed_reward_share=100, -# ip_ids=ip_ids, -# license_data={ -# 'license_terms_id': license_terms_id, -# 'license_template': PIL_LICENSE_TEMPLATE, -# 'licensing_config': { -# 'is_set': True, -# 'minting_fee': 0, -# 'hook_data': ZERO_ADDRESS, -# 'licensing_hook': ZERO_ADDRESS, -# 'commercial_rev_share': 10, -# 'disabled': False, -# 'expect_minimum_group_reward_share': 0, -# 'expect_group_reward_pool': ZERO_ADDRESS -# } -# } -# ) + # Register group and add IPs + result3 = story_client.Group.register_group_and_attach_license_and_add_ips( + group_pool=EVEN_SPLIT_GROUP_POOL, + max_allowed_reward_share=100, + ip_ids=ip_ids, + license_data={ + 'license_terms_id': license_terms_id, + 'license_template': PIL_LICENSE_TEMPLATE, + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 10, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) -# group_ip_id = result3['group_id'] + group_ip_id = result3['group_id'] -# # Create derivative IPs - Step 1: Mint and register -# result4 = story_client.IPAsset.mint_and_register_ip( -# spg_nft_contract=nft_collection, -# ip_metadata={ -# 'ip_metadata_uri': "test-derivative-uri-4", -# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-1")), -# 'nft_metadata_uri': "test-derivative-nft-uri-4", -# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-1")) -# } -# ) -# child_ip_id1 = result4['ip_id'] + # Create derivative IPs - Step 1: Mint and register + result4 = story_client.IPAsset.mint_and_register_ip( + spg_nft_contract=nft_collection, + ip_metadata={ + 'ip_metadata_uri': "test-derivative-uri-4", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-1")), + 'nft_metadata_uri': "test-derivative-nft-uri-4", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-1")) + } + ) + child_ip_id1 = result4['ip_id'] -# # Step 2: Register as derivative -# story_client.IPAsset.register_derivative( -# child_ip_id=child_ip_id1, -# parent_ip_ids=[group_ip_id], -# license_terms_ids=[license_terms_id], -# max_minting_fee=0, -# max_rts=10, -# max_revenue_share=0 -# ) + # Step 2: Register as derivative + story_client.IPAsset.register_derivative( + child_ip_id=child_ip_id1, + parent_ip_ids=[group_ip_id], + license_terms_ids=[license_terms_id], + max_minting_fee=0, + max_rts=10, + max_revenue_share=0 + ) -# # Create second derivative IP - Step 1: Mint and register -# result5 = story_client.IPAsset.mint_and_register_ip( -# spg_nft_contract=nft_collection, -# ip_metadata={ -# 'ip_metadata_uri': "test-derivative-uri-5", -# 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-2")), -# 'nft_metadata_uri': "test-derivative-nft-uri-5", -# 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-2")) -# } -# ) -# child_ip_id2 = result5['ip_id'] + # Create second derivative IP - Step 1: Mint and register + result5 = story_client.IPAsset.mint_and_register_ip( + spg_nft_contract=nft_collection, + ip_metadata={ + 'ip_metadata_uri': "test-derivative-uri-5", + 'ip_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-metadata-hash-2")), + 'nft_metadata_uri': "test-derivative-nft-uri-5", + 'nft_metadata_hash': web3.to_hex(web3.keccak(text="test-derivative-nft-metadata-hash-2")) + } + ) + child_ip_id2 = result5['ip_id'] -# # Step 2: Register as derivative -# story_client.IPAsset.register_derivative( -# child_ip_id=child_ip_id2, -# parent_ip_ids=[group_ip_id], -# license_terms_ids=[license_terms_id], -# max_minting_fee=0, -# max_rts=10, -# max_revenue_share=0 -# ) + # Step 2: Register as derivative + story_client.IPAsset.register_derivative( + child_ip_id=child_ip_id2, + parent_ip_ids=[group_ip_id], + license_terms_ids=[license_terms_id], + max_minting_fee=0, + max_rts=10, + max_revenue_share=0 + ) -# # Pay royalties from child IPs to group IP -# story_client.Royalty.pay_royalty_on_behalf( -# receiver_ip_id=child_ip_id1, -# payer_ip_id=group_ip_id, -# token=MockERC20, -# amount=100 -# ) + # Pay royalties from child IPs to group IP + story_client.Royalty.pay_royalty_on_behalf( + receiver_ip_id=child_ip_id1, + payer_ip_id=group_ip_id, + token=MockERC20, + amount=100 + ) -# story_client.Royalty.pay_royalty_on_behalf( -# receiver_ip_id=child_ip_id2, -# payer_ip_id=group_ip_id, -# token=MockERC20, -# amount=100 -# ) + story_client.Royalty.pay_royalty_on_behalf( + receiver_ip_id=child_ip_id2, + payer_ip_id=group_ip_id, + token=MockERC20, + amount=100 + ) -# print("claimable revenue is ", story_client.Royalty.claimable_revenue( -# royalty_vault_ip_id=child_ip_id1, -# claimer=account.address, -# token=MockERC20 -# )) + print("claimable revenue is ", story_client.Royalty.claimable_revenue( + royalty_vault_ip_id=child_ip_id1, + claimer=account.address, + token=MockERC20 + )) -# # Transfer to vault -# story_client.Royalty.transfer_to_vault( -# royalty_policy="LRP", -# ip_id=child_ip_id1, -# ancestor_ip_id=group_ip_id, -# token=MockERC20 -# ) + # Transfer to vault + story_client.Royalty.transfer_to_vault( + royalty_policy="LRP", + ip_id=child_ip_id1, + ancestor_ip_id=group_ip_id, + token=MockERC20 + ) -# story_client.Royalty.transfer_to_vault( -# royalty_policy="LRP", -# ip_id=child_ip_id2, -# ancestor_ip_id=group_ip_id, -# token=MockERC20 -# ) + story_client.Royalty.transfer_to_vault( + royalty_policy="LRP", + ip_id=child_ip_id2, + ancestor_ip_id=group_ip_id, + token=MockERC20 + ) -# return { -# 'group_ip_id': group_ip_id, -# 'ip_ids': ip_ids -# } + return { + 'group_ip_id': group_ip_id, + 'ip_ids': ip_ids + } -# def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): -# group_ip_id = setup_royalty_collection['group_ip_id'] -# ip_ids = setup_royalty_collection['ip_ids'] + def test_collect_and_distribute_group_royalties(self, story_client, setup_royalty_collection): + group_ip_id = setup_royalty_collection['group_ip_id'] + ip_ids = setup_royalty_collection['ip_ids'] -# response = story_client.Group.collect_and_distribute_group_royalties( -# group_ip_id=group_ip_id, -# currency_tokens=[MockERC20], -# member_ip_ids=ip_ids -# ) + response = story_client.Group.collect_and_distribute_group_royalties( + group_ip_id=group_ip_id, + currency_tokens=[MockERC20], + member_ip_ids=ip_ids + ) -# assert 'tx_hash' in response -# assert isinstance(response['tx_hash'], str) -# assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 -# assert 'collected_royalties' in response -# assert len(response['collected_royalties']) > 0 -# assert response['collected_royalties'][0]['amount'] == 20 + assert 'collected_royalties' in response + assert len(response['collected_royalties']) > 0 + assert response['collected_royalties'][0]['amount'] == 20 -# assert 'royalties_distributed' in response -# assert len(response['royalties_distributed']) == 2 -# assert response['royalties_distributed'][0]['amount'] == 10 -# assert response['royalties_distributed'][1]['amount'] == 10 + assert 'royalties_distributed' in response + assert len(response['royalties_distributed']) == 2 + assert response['royalties_distributed'][0]['amount'] == 10 + assert response['royalties_distributed'][1]['amount'] == 10 From fa58f0b8b47765c019dc01c4fdfa95ab16302cc4 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 17 Apr 2025 14:35:10 +0900 Subject: [PATCH 18/26] uncommented integration tests for test_register_group_and_attach_license_and_add_ips() --- tests/integration/test_integration_group.py | 90 ++++++++++----------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 5a2fc97..5780e00 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -277,54 +277,54 @@ def test_register_ip_and_attach_license_and_add_to_group(self, story_client, gro assert isinstance(response['ip_id'], str) assert response['ip_id'].startswith("0x") - # def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): - # response = story_client.Group.register_group_and_attach_license_and_add_ips( - # group_pool=EVEN_SPLIT_GROUP_POOL, - # max_allowed_reward_share=5, - # ip_ids=[ip_with_license['ip_id']], - # license_data={ - # 'license_terms_id': ip_with_license['license_terms_id'], - # 'licensing_config': { - # 'is_set': True, - # 'minting_fee': 0, - # 'hook_data': ZERO_ADDRESS, - # 'licensing_hook': ZERO_ADDRESS, - # 'commercial_rev_share': 0, - # 'disabled': False, - # 'expect_minimum_group_reward_share': 0, - # 'expect_group_reward_pool': ZERO_ADDRESS - # } - # } - # ) + def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): + response = story_client.Group.register_group_and_attach_license_and_add_ips( + group_pool=EVEN_SPLIT_GROUP_POOL, + max_allowed_reward_share=5, + ip_ids=[ip_with_license['ip_id']], + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) - # assert 'tx_hash' in response - # assert isinstance(response['tx_hash'], str) - # assert len(response['tx_hash']) > 0 + assert 'tx_hash' in response + assert isinstance(response['tx_hash'], str) + assert len(response['tx_hash']) > 0 - # assert 'group_id' in response - # assert isinstance(response['group_id'], str) - # assert response['group_id'].startswith("0x") + assert 'group_id' in response + assert isinstance(response['group_id'], str) + assert response['group_id'].startswith("0x") - # def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): - # with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): - # story_client.Group.register_group_and_attach_license_and_add_ips( - # group_pool=EVEN_SPLIT_GROUP_POOL, - # max_allowed_reward_share=5, - # ip_ids=[ZERO_ADDRESS], # Invalid IP address - # license_data={ - # 'license_terms_id': ip_with_license['license_terms_id'], - # 'licensing_config': { - # 'is_set': True, - # 'minting_fee': 0, - # 'hook_data': ZERO_ADDRESS, - # 'licensing_hook': ZERO_ADDRESS, - # 'commercial_rev_share': 0, - # 'disabled': False, - # 'expect_minimum_group_reward_share': 0, - # 'expect_group_reward_pool': ZERO_ADDRESS - # } - # } - # ) + def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): + with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): + story_client.Group.register_group_and_attach_license_and_add_ips( + group_pool=EVEN_SPLIT_GROUP_POOL, + max_allowed_reward_share=5, + ip_ids=[ZERO_ADDRESS], # Invalid IP address + license_data={ + 'license_terms_id': ip_with_license['license_terms_id'], + 'licensing_config': { + 'is_set': True, + 'minting_fee': 0, + 'hook_data': ZERO_ADDRESS, + 'licensing_hook': ZERO_ADDRESS, + 'commercial_rev_share': 0, + 'disabled': False, + 'expect_minimum_group_reward_share': 0, + 'expect_group_reward_pool': ZERO_ADDRESS + } + } + ) class TestCollectRoyaltyAndClaimReward: @pytest.fixture(scope="module") From f69431ea1445022a34b6e38f8088b934678ff9e6 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Tue, 22 Apr 2025 02:03:51 +0900 Subject: [PATCH 19/26] Updated transfer_to_vault() to use LRP abi --- .../RoyaltyPolicyLRP_client.py | 7 + .../abi/jsons/RoyaltyPolicyLAP.json | 825 ++++++++--------- .../abi/jsons/RoyaltyPolicyLRP.json | 849 +++++++++--------- .../resources/Royalty.py | 6 +- .../scripts/config.json | 2 +- tests/integration/test_integration_group.py | 572 ++++++------ 6 files changed, 1139 insertions(+), 1122 deletions(-) diff --git a/src/story_protocol_python_sdk/abi/RoyaltyPolicyLRP/RoyaltyPolicyLRP_client.py b/src/story_protocol_python_sdk/abi/RoyaltyPolicyLRP/RoyaltyPolicyLRP_client.py index 3cbd10a..64f5c3d 100644 --- a/src/story_protocol_python_sdk/abi/RoyaltyPolicyLRP/RoyaltyPolicyLRP_client.py +++ b/src/story_protocol_python_sdk/abi/RoyaltyPolicyLRP/RoyaltyPolicyLRP_client.py @@ -21,4 +21,11 @@ def __init__(self, web3: Web3): with open(abi_path, 'r') as abi_file: abi = json.load(abi_file) self.contract = self.web3.eth.contract(address=contract_address, abi=abi) + + def transferToVault(self, ipId, ancestorIpId, token): + return self.contract.functions.transferToVault(ipId, ancestorIpId, token).transact() + + def build_transferToVault_transaction(self, ipId, ancestorIpId, token, tx_params): + return self.contract.functions.transferToVault(ipId, ancestorIpId, token).build_transaction(tx_params) + \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLAP.json b/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLAP.json index 23c3f88..8ff970d 100644 --- a/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLAP.json +++ b/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLAP.json @@ -1,644 +1,651 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "royaltyModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipGraphAcl", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AccessManagedInvalidAuthority", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "AccessManagedRequiredDelay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "AccessManagedUnauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "EnforcedPause", + "type": "error" + }, + { + "inputs": [], + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__AboveMaxPercent", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__CallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__NotRoyaltyModule", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__SameIpTransfer", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__ZeroClaimableRoyalty", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__ZeroIPGraphACL", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLAP__ZeroRoyaltyModule", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" }, { - "type": "function", - "name": "IP_GRAPH", "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AuthorityUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "ancestorIpId", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RevenueTransferredToVault", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "IP_GRAPH", "outputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "IP_GRAPH_ACL", "inputs": [], + "name": "IP_GRAPH_ACL", "outputs": [ { + "internalType": "contract IPGraphACL", "name": "", - "type": "address", - "internalType": "contract IPGraphACL" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "ROYALTY_MODULE", "inputs": [], + "name": "ROYALTY_MODULE", "outputs": [ { + "internalType": "contract IRoyaltyModule", "name": "", - "type": "address", - "internalType": "contract IRoyaltyModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", "outputs": [ { + "internalType": "string", "name": "", - "type": "string", - "internalType": "string" + "type": "string" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "__ProtocolPausable_init", "inputs": [ { + "internalType": "address", "name": "accessManager", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "__ProtocolPausable_init", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "authority", "inputs": [], + "name": "authority", "outputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getPolicyRoyalty", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getPolicyRoyalty", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "getPolicyRoyaltyStack", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getPolicyRoyaltyStack", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getPolicyRtsRequiredToLink", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint32", "name": "licensePercent", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], + "name": "getPolicyRtsRequiredToLink", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getTransferredTokens", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "token", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getTransferredTokens", "outputs": [ { + "internalType": "uint256", "name": "", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "initialize", "inputs": [ { + "internalType": "address", "name": "accessManager", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "initialize", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", + "inputs": [], "name": "isConsumingScheduledOp", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], + "name": "isSupportGroup", "outputs": [ { + "internalType": "bool", "name": "", - "type": "bytes4", - "internalType": "bytes4" + "type": "bool" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "onLicenseMinting", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint32", "name": "licensePercent", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bytes", "name": "", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "onLicenseMinting", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "onLinkToParents", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address[]", "name": "parentIpIds", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" }, { + "internalType": "address[]", "name": "licenseRoyaltyPolicies", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" }, { + "internalType": "uint32[]", "name": "licensesPercent", - "type": "uint32[]", - "internalType": "uint32[]" + "type": "uint32[]" }, { + "internalType": "bytes", "name": "", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "onLinkToParents", "outputs": [ { + "internalType": "uint32", "name": "newRoyaltyStackLAP", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "pause", "inputs": [], + "name": "pause", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "paused", "inputs": [], + "name": "paused", "outputs": [ { + "internalType": "bool", "name": "", - "type": "bool", - "internalType": "bool" + "type": "bool" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "proxiableUUID", "inputs": [], + "name": "proxiableUUID", "outputs": [ { + "internalType": "bytes32", "name": "", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "setAuthority", "inputs": [ { + "internalType": "address", "name": "newAuthority", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "setAuthority", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "transferToVault", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "token", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "transferToVault", "outputs": [ { + "internalType": "uint256", "name": "", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "unpause", "inputs": [], + "name": "unpause", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "upgradeToAndCall", "inputs": [ { + "internalType": "address", "name": "newImplementation", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "data", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "upgradeToAndCall", "outputs": [], - "stateMutability": "payable" - }, - { - "type": "event", - "name": "AuthorityUpdated", - "inputs": [ - { - "name": "authority", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ - { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Paused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "RevenueTransferredToVault", - "inputs": [ - { - "name": "ipId", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "ancestorIpId", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "token", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "amount", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Unpaused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ - { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AccessManagedInvalidAuthority", - "inputs": [ - { - "name": "authority", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AccessManagedRequiredDelay", - "inputs": [ - { - "name": "caller", - "type": "address", - "internalType": "address" - }, - { - "name": "delay", - "type": "uint32", - "internalType": "uint32" - } - ] - }, - { - "type": "error", - "name": "AccessManagedUnauthorized", - "inputs": [ - { - "name": "caller", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ - { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AddressInsufficientBalance", - "inputs": [ - { - "name": "account", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ - { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__AboveMaxPercent", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__CallFailed", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__NotRoyaltyModule", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__ZeroAccessManager", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__ZeroClaimableRoyalty", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__ZeroIPGraphACL", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLAP__ZeroRoyaltyModule", - "inputs": [] - }, - { - "type": "error", - "name": "SafeERC20FailedOperation", - "inputs": [ - { - "name": "token", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ - { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" - } - ] + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file diff --git a/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLRP.json b/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLRP.json index 2026a7e..30795e4 100644 --- a/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLRP.json +++ b/src/story_protocol_python_sdk/abi/jsons/RoyaltyPolicyLRP.json @@ -1,667 +1,674 @@ [ { - "type": "constructor", "inputs": [ { + "internalType": "address", "name": "royaltyModule", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "royaltyPolicyLAP", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ipGraphAcl", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AccessManagedInvalidAuthority", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "AccessManagedRequiredDelay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "AccessManagedUnauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "EnforcedPause", + "type": "error" + }, + { + "inputs": [], + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "FailedCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__AboveMaxPercent", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__CallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__NotRoyaltyModule", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__SameIpTransfer", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__ZeroAccessManager", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__ZeroClaimableRoyalty", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__ZeroIPGraphACL", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__ZeroRoyaltyModule", + "type": "error" + }, + { + "inputs": [], + "name": "RoyaltyPolicyLRP__ZeroRoyaltyPolicyLAP", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" }, { - "type": "function", - "name": "IP_GRAPH", "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "AuthorityUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "ipId", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "ancestorIpId", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RevenueTransferredToVault", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "IP_GRAPH", "outputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "IP_GRAPH_ACL", "inputs": [], + "name": "IP_GRAPH_ACL", "outputs": [ { + "internalType": "contract IPGraphACL", "name": "", - "type": "address", - "internalType": "contract IPGraphACL" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "ROYALTY_MODULE", "inputs": [], + "name": "ROYALTY_MODULE", "outputs": [ { + "internalType": "contract IRoyaltyModule", "name": "", - "type": "address", - "internalType": "contract IRoyaltyModule" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "ROYALTY_POLICY_LAP", "inputs": [], + "name": "ROYALTY_POLICY_LAP", "outputs": [ { + "internalType": "contract IGraphAwareRoyaltyPolicy", "name": "", - "type": "address", - "internalType": "contract IGraphAwareRoyaltyPolicy" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", "outputs": [ { + "internalType": "string", "name": "", - "type": "string", - "internalType": "string" + "type": "string" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "__ProtocolPausable_init", "inputs": [ { + "internalType": "address", "name": "accessManager", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "__ProtocolPausable_init", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "authority", "inputs": [], + "name": "authority", "outputs": [ { + "internalType": "address", "name": "", - "type": "address", - "internalType": "address" + "type": "address" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getPolicyRoyalty", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getPolicyRoyalty", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "getPolicyRoyaltyStack", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getPolicyRoyaltyStack", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getPolicyRtsRequiredToLink", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint32", "name": "licensePercent", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], + "name": "getPolicyRtsRequiredToLink", "outputs": [ { + "internalType": "uint32", "name": "", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "getTransferredTokens", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "token", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "getTransferredTokens", "outputs": [ { + "internalType": "uint256", "name": "", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "initialize", "inputs": [ { + "internalType": "address", "name": "accessManager", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "initialize", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", + "inputs": [], "name": "isConsumingScheduledOp", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], + "name": "isSupportGroup", "outputs": [ { + "internalType": "bool", "name": "", - "type": "bytes4", - "internalType": "bytes4" + "type": "bool" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "onLicenseMinting", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "uint32", "name": "licensePercent", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" }, { + "internalType": "bytes", "name": "", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "onLicenseMinting", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "onLinkToParents", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address[]", "name": "parentIpIds", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" }, { + "internalType": "address[]", "name": "licenseRoyaltyPolicies", - "type": "address[]", - "internalType": "address[]" + "type": "address[]" }, { + "internalType": "uint32[]", "name": "licensesPercent", - "type": "uint32[]", - "internalType": "uint32[]" + "type": "uint32[]" }, { + "internalType": "bytes", "name": "", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "onLinkToParents", "outputs": [ { + "internalType": "uint32", "name": "newRoyaltyStackLRP", - "type": "uint32", - "internalType": "uint32" + "type": "uint32" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "pause", "inputs": [], + "name": "pause", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "paused", "inputs": [], + "name": "paused", "outputs": [ { + "internalType": "bool", "name": "", - "type": "bool", - "internalType": "bool" + "type": "bool" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "proxiableUUID", "inputs": [], + "name": "proxiableUUID", "outputs": [ { + "internalType": "bytes32", "name": "", - "type": "bytes32", - "internalType": "bytes32" + "type": "bytes32" } ], - "stateMutability": "view" + "stateMutability": "view", + "type": "function" }, { - "type": "function", - "name": "setAuthority", "inputs": [ { + "internalType": "address", "name": "newAuthority", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "setAuthority", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "transferToVault", "inputs": [ { + "internalType": "address", "name": "ipId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "ancestorIpId", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "address", "name": "token", - "type": "address", - "internalType": "address" + "type": "address" } ], + "name": "transferToVault", "outputs": [ { + "internalType": "uint256", "name": "", - "type": "uint256", - "internalType": "uint256" + "type": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "unpause", "inputs": [], + "name": "unpause", "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "nonpayable", + "type": "function" }, { - "type": "function", - "name": "upgradeToAndCall", "inputs": [ { + "internalType": "address", "name": "newImplementation", - "type": "address", - "internalType": "address" + "type": "address" }, { + "internalType": "bytes", "name": "data", - "type": "bytes", - "internalType": "bytes" + "type": "bytes" } ], + "name": "upgradeToAndCall", "outputs": [], - "stateMutability": "payable" - }, - { - "type": "event", - "name": "AuthorityUpdated", - "inputs": [ - { - "name": "authority", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ - { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Paused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "RevenueTransferredToVault", - "inputs": [ - { - "name": "ipId", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "ancestorIpId", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "token", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "amount", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Unpaused", - "inputs": [ - { - "name": "account", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ - { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AccessManagedInvalidAuthority", - "inputs": [ - { - "name": "authority", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AccessManagedRequiredDelay", - "inputs": [ - { - "name": "caller", - "type": "address", - "internalType": "address" - }, - { - "name": "delay", - "type": "uint32", - "internalType": "uint32" - } - ] - }, - { - "type": "error", - "name": "AccessManagedUnauthorized", - "inputs": [ - { - "name": "caller", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ - { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "AddressInsufficientBalance", - "inputs": [ - { - "name": "account", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ - { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "EnforcedPause", - "inputs": [] - }, - { - "type": "error", - "name": "ExpectedPause", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__AboveMaxPercent", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__CallFailed", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__NotRoyaltyModule", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__ZeroAccessManager", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__ZeroClaimableRoyalty", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__ZeroIPGraphACL", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__ZeroRoyaltyModule", - "inputs": [] - }, - { - "type": "error", - "name": "RoyaltyPolicyLRP__ZeroRoyaltyPolicyLAP", - "inputs": [] - }, - { - "type": "error", - "name": "SafeERC20FailedOperation", - "inputs": [ - { - "name": "token", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ - { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" - } - ] + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file diff --git a/src/story_protocol_python_sdk/resources/Royalty.py b/src/story_protocol_python_sdk/resources/Royalty.py index f137d24..aa52d4f 100644 --- a/src/story_protocol_python_sdk/resources/Royalty.py +++ b/src/story_protocol_python_sdk/resources/Royalty.py @@ -9,6 +9,7 @@ from story_protocol_python_sdk.abi.RoyaltyWorkflows.RoyaltyWorkflows_client import RoyaltyWorkflowsClient from story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client import IPAccountImplClient from story_protocol_python_sdk.abi.MockERC20.MockERC20_client import MockERC20Client +from story_protocol_python_sdk.abi.RoyaltyPolicyLRP.RoyaltyPolicyLRP_client import RoyaltyPolicyLRPClient from story_protocol_python_sdk.utils.transaction_utils import build_and_send_transaction @@ -32,6 +33,7 @@ def __init__(self, web3: Web3, account, chain_id: int): self.royalty_workflows_client = RoyaltyWorkflowsClient(web3) self.ip_account_impl_client = IPAccountImplClient(web3) self.mock_erc20_client = MockERC20Client(web3) + self.royalty_policy_lrp_client = RoyaltyPolicyLRPClient(web3) def get_royalty_vault_address(self, ip_id: str) -> str: """ @@ -206,9 +208,7 @@ def transfer_to_vault( if royalty_policy == "LAP": royalty_policy_client = self.royalty_policy_lap_client elif royalty_policy == "LRP": - royalty_policy_client = self.royalty_policy_lap_client # Same ABI for all royalty policies - # Override the contract address for LRP - royalty_policy_client.contract.address = "0x9156e603C949481883B1d3355c6f1132D191fC41" + royalty_policy_client = self.royalty_policy_lrp_client else: # If it's a custom address if not self.web3.is_address(royalty_policy): diff --git a/src/story_protocol_python_sdk/scripts/config.json b/src/story_protocol_python_sdk/scripts/config.json index 66c58c8..a99c878 100644 --- a/src/story_protocol_python_sdk/scripts/config.json +++ b/src/story_protocol_python_sdk/scripts/config.json @@ -207,7 +207,7 @@ { "contract_name": "RoyaltyPolicyLRP", "contract_address": "0x9156e603C949481883B1d3355c6f1132D191fC41", - "functions": [] + "functions": ["transferToVault"] }, { "contract_name": "ArbitrationPolicyUMA", diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index 5780e00..c5aee36 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -17,314 +17,314 @@ ROYALTY_POLICY_LRP ) -class TestGroupBasicOperations: - def test_register_basic_group(self, story_client): - response = story_client.Group.register_group( - group_pool=EVEN_SPLIT_GROUP_POOL - ) +# class TestGroupBasicOperations: +# def test_register_basic_group(self, story_client): +# response = story_client.Group.register_group( +# group_pool=EVEN_SPLIT_GROUP_POOL +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 - assert 'group_id' in response - assert isinstance(response['group_id'], str) - assert response['group_id'].startswith("0x") +# assert 'group_id' in response +# assert isinstance(response['group_id'], str) +# assert response['group_id'].startswith("0x") -class TestGroupWithLicenseOperations: - @pytest.fixture(scope="module") - def nft_collection(self, story_client): - tx_data = story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - max_supply=100, - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=account.address, - ) - return tx_data['nft_contract'] +# class TestGroupWithLicenseOperations: +# @pytest.fixture(scope="module") +# def nft_collection(self, story_client): +# tx_data = story_client.NFTClient.create_nft_collection( +# name="test-collection", +# symbol="TEST", +# max_supply=100, +# is_public_minting=True, +# mint_open=True, +# contract_uri="test-uri", +# mint_fee_recipient=account.address, +# ) +# return tx_data['nft_contract'] - @pytest.fixture(scope="module") - def ip_with_license(self, story_client, nft_collection): - # Create initial IP with license terms - response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( - spg_nft_contract=nft_collection, - terms=[{ - 'terms': { - 'transferable': True, - 'royalty_policy': ROYALTY_POLICY_LRP, - 'default_minting_fee': 0, - 'expiration': 1000, - 'commercial_use': True, - 'commercial_attribution': False, - 'commercializer_checker': ZERO_ADDRESS, - 'commercializer_checker_data': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'commercial_rev_ceiling': 0, - 'derivatives_allowed': True, - 'derivatives_attribution': True, - 'derivatives_approval': False, - 'derivatives_reciprocal': True, - 'derivative_rev_ceiling': 0, - 'currency': MockERC20, - 'uri': "test case" - }, - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }] - ) +# @pytest.fixture(scope="module") +# def ip_with_license(self, story_client, nft_collection): +# # Create initial IP with license terms +# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=[{ +# 'terms': { +# 'transferable': True, +# 'royalty_policy': ROYALTY_POLICY_LRP, +# 'default_minting_fee': 0, +# 'expiration': 1000, +# 'commercial_use': True, +# 'commercial_attribution': False, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'commercial_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_attribution': True, +# 'derivatives_approval': False, +# 'derivatives_reciprocal': True, +# 'derivative_rev_ceiling': 0, +# 'currency': MockERC20, +# 'uri': "test case" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) - ip_id = response['ip_id'] - license_terms_id = response['license_terms_ids'][0] +# ip_id = response['ip_id'] +# license_terms_id = response['license_terms_ids'][0] - licensing_config = { - 'isSet': True, - 'mintingFee': 0, - 'licensingHook': ZERO_ADDRESS, - 'hookData': ZERO_ADDRESS, - 'commercialRevShare': 0, - 'disabled': False, - 'expectMinimumGroupRewardShare': 0, - 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL - } +# licensing_config = { +# 'isSet': True, +# 'mintingFee': 0, +# 'licensingHook': ZERO_ADDRESS, +# 'hookData': ZERO_ADDRESS, +# 'commercialRevShare': 0, +# 'disabled': False, +# 'expectMinimumGroupRewardShare': 0, +# 'expectGroupRewardPool': EVEN_SPLIT_GROUP_POOL +# } - # Set licensing config - story_client.License.set_licensing_config( - ip_id=ip_id, - license_terms_id=license_terms_id, - license_template=PIL_LICENSE_TEMPLATE, - licensing_config=licensing_config - ) +# # Set licensing config +# story_client.License.set_licensing_config( +# ip_id=ip_id, +# license_terms_id=license_terms_id, +# license_template=PIL_LICENSE_TEMPLATE, +# licensing_config=licensing_config +# ) - return { - 'ip_id': ip_id, - 'license_terms_id': license_terms_id - } +# return { +# 'ip_id': ip_id, +# 'license_terms_id': license_terms_id +# } - @pytest.fixture(scope="module") - def group_with_license(self, story_client, ip_with_license): - response = story_client.Group.register_group_and_attach_license( - group_pool=EVEN_SPLIT_GROUP_POOL, - license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS - } - } - ) +# @pytest.fixture(scope="module") +# def group_with_license(self, story_client, ip_with_license): +# response = story_client.Group.register_group_and_attach_license( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) - assert response is not None - assert 'group_id' in response - assert response['group_id'] is not None - return response['group_id'] +# assert response is not None +# assert 'group_id' in response +# assert response['group_id'] is not None +# return response['group_id'] - def test_register_group_and_attach_license(self, group_with_license): - assert group_with_license is not None +# def test_register_group_and_attach_license(self, group_with_license): +# assert group_with_license is not None - def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): - response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( - group_id=group_with_license, - spg_nft_contract=nft_collection, - license_data=[{ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }], - max_allowed_reward_share=5 - ) +# def test_mint_register_ip_attach_license_add_to_group(self, story_client, group_with_license, ip_with_license, nft_collection): +# response = story_client.Group.mint_and_register_ip_and_attach_license_and_add_to_group( +# group_id=group_with_license, +# spg_nft_contract=nft_collection, +# license_data=[{ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }], +# max_allowed_reward_share=5 +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 - assert 'ip_id' in response - assert isinstance(response['ip_id'], str) - assert response['ip_id'].startswith("0x") +# assert 'ip_id' in response +# assert isinstance(response['ip_id'], str) +# assert response['ip_id'].startswith("0x") -class TestAdvancedGroupOperations: - @pytest.fixture(scope="module") - def nft_collection(self, story_client): - tx_data = story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - max_supply=100, - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=account.address, - ) - return tx_data['nft_contract'] +# class TestAdvancedGroupOperations: +# @pytest.fixture(scope="module") +# def nft_collection(self, story_client): +# tx_data = story_client.NFTClient.create_nft_collection( +# name="test-collection", +# symbol="TEST", +# max_supply=100, +# is_public_minting=True, +# mint_open=True, +# contract_uri="test-uri", +# mint_fee_recipient=account.address, +# ) +# return tx_data['nft_contract'] - @pytest.fixture(scope="module") - def ip_with_license(self, story_client, nft_collection): - response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( - spg_nft_contract=nft_collection, - terms=[{ - 'terms': { - 'transferable': True, - 'royalty_policy': ROYALTY_POLICY_LRP, - 'default_minting_fee': 0, - 'expiration': 1000, - 'commercial_use': True, - 'commercial_attribution': False, - 'commercializer_checker': ZERO_ADDRESS, - 'commercializer_checker_data': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'commercial_rev_ceiling': 0, - 'derivatives_allowed': True, - 'derivatives_attribution': True, - 'derivatives_approval': False, - 'derivatives_reciprocal': True, - 'derivative_rev_ceiling': 0, - 'currency': MockERC20, - 'uri': "test case" - }, - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }] - ) +# @pytest.fixture(scope="module") +# def ip_with_license(self, story_client, nft_collection): +# response = story_client.IPAsset.mint_and_register_ip_asset_with_pil_terms( +# spg_nft_contract=nft_collection, +# terms=[{ +# 'terms': { +# 'transferable': True, +# 'royalty_policy': ROYALTY_POLICY_LRP, +# 'default_minting_fee': 0, +# 'expiration': 1000, +# 'commercial_use': True, +# 'commercial_attribution': False, +# 'commercializer_checker': ZERO_ADDRESS, +# 'commercializer_checker_data': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'commercial_rev_ceiling': 0, +# 'derivatives_allowed': True, +# 'derivatives_attribution': True, +# 'derivatives_approval': False, +# 'derivatives_reciprocal': True, +# 'derivative_rev_ceiling': 0, +# 'currency': MockERC20, +# 'uri': "test case" +# }, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) - return { - 'ip_id': response['ip_id'], - 'license_terms_id': response['license_terms_ids'][0] - } +# return { +# 'ip_id': response['ip_id'], +# 'license_terms_id': response['license_terms_ids'][0] +# } - @pytest.fixture(scope="module") - def group_id(self, story_client, ip_with_license): - # Create a group with license attached - response = story_client.Group.register_group_and_attach_license( - group_pool=EVEN_SPLIT_GROUP_POOL, - license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], - 'license_template': PIL_LICENSE_TEMPLATE, - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS - } - } - ) - return response['group_id'] +# @pytest.fixture(scope="module") +# def group_id(self, story_client, ip_with_license): +# # Create a group with license attached +# response = story_client.Group.register_group_and_attach_license( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'license_template': PIL_LICENSE_TEMPLATE, +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) +# return response['group_id'] - def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): - token_id = get_token_id(MockERC721, story_client.web3, story_client.account) +# def test_register_ip_and_attach_license_and_add_to_group(self, story_client, group_id, ip_with_license): +# token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - response = story_client.Group.register_ip_and_attach_license_and_add_to_group( - group_id=group_id, - nft_contract=MockERC721, - token_id=token_id, - max_allowed_reward_share=5, - license_data=[{ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL - } - }] - ) +# response = story_client.Group.register_ip_and_attach_license_and_add_to_group( +# group_id=group_id, +# nft_contract=MockERC721, +# token_id=token_id, +# max_allowed_reward_share=5, +# license_data=[{ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': EVEN_SPLIT_GROUP_POOL +# } +# }] +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 - assert 'ip_id' in response - assert isinstance(response['ip_id'], str) - assert response['ip_id'].startswith("0x") +# assert 'ip_id' in response +# assert isinstance(response['ip_id'], str) +# assert response['ip_id'].startswith("0x") - def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): - response = story_client.Group.register_group_and_attach_license_and_add_ips( - group_pool=EVEN_SPLIT_GROUP_POOL, - max_allowed_reward_share=5, - ip_ids=[ip_with_license['ip_id']], - license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS - } - } - ) +# def test_register_group_and_attach_license_and_add_ips(self, story_client, ip_with_license): +# response = story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=5, +# ip_ids=[ip_with_license['ip_id']], +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) - assert 'tx_hash' in response - assert isinstance(response['tx_hash'], str) - assert len(response['tx_hash']) > 0 +# assert 'tx_hash' in response +# assert isinstance(response['tx_hash'], str) +# assert len(response['tx_hash']) > 0 - assert 'group_id' in response - assert isinstance(response['group_id'], str) - assert response['group_id'].startswith("0x") +# assert 'group_id' in response +# assert isinstance(response['group_id'], str) +# assert response['group_id'].startswith("0x") - def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): - with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): - story_client.Group.register_group_and_attach_license_and_add_ips( - group_pool=EVEN_SPLIT_GROUP_POOL, - max_allowed_reward_share=5, - ip_ids=[ZERO_ADDRESS], # Invalid IP address - license_data={ - 'license_terms_id': ip_with_license['license_terms_id'], - 'licensing_config': { - 'is_set': True, - 'minting_fee': 0, - 'hook_data': ZERO_ADDRESS, - 'licensing_hook': ZERO_ADDRESS, - 'commercial_rev_share': 0, - 'disabled': False, - 'expect_minimum_group_reward_share': 0, - 'expect_group_reward_pool': ZERO_ADDRESS - } - } - ) +# def test_fail_add_unregistered_ip_to_group(self, story_client, ip_with_license): +# with pytest.raises(ValueError, match="Failed to register group and attach license and add IPs"): +# story_client.Group.register_group_and_attach_license_and_add_ips( +# group_pool=EVEN_SPLIT_GROUP_POOL, +# max_allowed_reward_share=5, +# ip_ids=[ZERO_ADDRESS], # Invalid IP address +# license_data={ +# 'license_terms_id': ip_with_license['license_terms_id'], +# 'licensing_config': { +# 'is_set': True, +# 'minting_fee': 0, +# 'hook_data': ZERO_ADDRESS, +# 'licensing_hook': ZERO_ADDRESS, +# 'commercial_rev_share': 0, +# 'disabled': False, +# 'expect_minimum_group_reward_share': 0, +# 'expect_group_reward_pool': ZERO_ADDRESS +# } +# } +# ) class TestCollectRoyaltyAndClaimReward: @pytest.fixture(scope="module") @@ -485,13 +485,7 @@ def setup_royalty_collection(self, story_client, nft_collection): token=MockERC20, amount=100 ) - - print("claimable revenue is ", story_client.Royalty.claimable_revenue( - royalty_vault_ip_id=child_ip_id1, - claimer=account.address, - token=MockERC20 - )) - + # Transfer to vault story_client.Royalty.transfer_to_vault( royalty_policy="LRP", @@ -521,6 +515,8 @@ def test_collect_and_distribute_group_royalties(self, story_client, setup_royalt currency_tokens=[MockERC20], member_ip_ids=ip_ids ) + + print("response is ", response) assert 'tx_hash' in response assert isinstance(response['tx_hash'], str) From bc16e28fce4f43ef2cf7fa1f0fde8cdd6a9833cb Mon Sep 17 00:00:00 2001 From: Boris Polania Date: Tue, 22 Apr 2025 17:07:05 -0700 Subject: [PATCH 20/26] Debugging Groups Module Integration Tests (#67) * Debugging * Update test_integration_ip_account.py --- .../resources/Group.py | 20 ++++++++--------- tests/integration/test_integration_group.py | 3 ++- .../test_integration_ip_account.py | 10 ++++----- .../test_integration_nft_client.py | 22 +++++++++---------- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 079d894..b64626c 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -472,7 +472,7 @@ def collect_and_distribute_group_royalties( member_ip_ids, tx_options=tx_options ) - + # Parse events to get collected royalties collected_royalties = self._parse_tx_collected_royalties_to_group_pool_event(response['tx_receipt']) royalties_distributed = self._parse_tx_royalty_paid_event(response['tx_receipt']) @@ -607,14 +607,14 @@ def _parse_tx_collected_royalties_to_group_pool_event(self, tx_receipt: dict) -> :param tx_receipt dict: The transaction receipt. :return list: List of collected royalties. """ - event_signature = self.web3.keccak(text="CollectedRoyaltiesToGroupPool(address,uint256,address)").hex() + event_signature = self.web3.keccak(text="CollectedRoyaltiesToGroupPool(address,address,address,uint256)").hex() collected_royalties = [] - + for log in tx_receipt['logs']: if log['topics'][0].hex() == event_signature: group_id = '0x' + log['topics'][1].hex()[24:] - amount = int(log['data'][:66], 16) - token = '0x' + log['data'][90:130] + amount = int(log['data'][:66].hex(), 16) + token = '0x' + log['topics'][2].hex()[24:] collected_royalties.append({ 'group_id': self.web3.to_checksum_address(group_id), @@ -631,16 +631,16 @@ def _parse_tx_royalty_paid_event(self, tx_receipt: dict) -> list: :param tx_receipt dict: The transaction receipt. :return list: List of royalties distributed. """ - event_signature = self.web3.keccak(text="RoyaltyPaid(address,uint256,address,uint256)").hex() + event_signature = self.web3.keccak(text="RoyaltyPaid(address,address,address,address,uint256,uint256)").hex() royalties_distributed = [] for log in tx_receipt['logs']: if log['topics'][0].hex() == event_signature: - receiver_ip_id = '0x' + log['topics'][1].hex()[24:] + receiver_ip_id = '0x' + log['topics'][0].hex()[24:] data = log['data'] - amount = int(data[:66], 16) - token = '0x' + data[90:130] - amount_after_fee = int(data[154:], 16) + amount = int(data[128:160].hex(), 16) + token = '0x' + data[108:128].hex() + amount_after_fee = int(data[160:].hex(), 16) royalties_distributed.append({ 'ip_id': self.web3.to_checksum_address(receiver_ip_id), diff --git a/tests/integration/test_integration_group.py b/tests/integration/test_integration_group.py index c5aee36..0aa10b3 100644 --- a/tests/integration/test_integration_group.py +++ b/tests/integration/test_integration_group.py @@ -500,7 +500,7 @@ def setup_royalty_collection(self, story_client, nft_collection): ancestor_ip_id=group_ip_id, token=MockERC20 ) - + return { 'group_ip_id': group_ip_id, 'ip_ids': ip_ids @@ -523,6 +523,7 @@ def test_collect_and_distribute_group_royalties(self, story_client, setup_royalt assert len(response['tx_hash']) > 0 assert 'collected_royalties' in response + assert len(response['collected_royalties']) > 0 assert response['collected_royalties'][0]['amount'] == 20 diff --git a/tests/integration/test_integration_ip_account.py b/tests/integration/test_integration_ip_account.py index b15c47d..9f0c42d 100644 --- a/tests/integration/test_integration_ip_account.py +++ b/tests/integration/test_integration_ip_account.py @@ -420,10 +420,10 @@ def test_execute_with_sig_wrong_signer(self, story_client): nft_contract=MockERC721, token_id=token_id ) - ip_id = register_response['ipId'] + ip_id = register_response['ip_id'] deadline = get_block_timestamp(web3) + 100 - state = story_client.IPAccount.getIpAccountNonce(ip_id) + state = story_client.IPAccount.get_ip_account_nonce(ip_id) data = "0x" execute_data = story_client.IPAccount.ip_account_client.contract.encode_abi( @@ -472,7 +472,7 @@ def test_execute_with_sig_wrong_signer(self, story_client): wrong_signer = "0x1234567890123456789012345678901234567890" with pytest.raises(Exception) as exc_info: - story_client.IPAccount.executeWithSig( + story_client.IPAccount.execute_with_sig( ip_id=ip_id, to=story_client.IPAccount.access_controller_client.contract.address, value=0, @@ -509,10 +509,10 @@ def test_transfer_erc20_invalid_token_params(self, story_client): nft_contract=MockERC721, token_id=token_id ) - ip_id = register_response['ipId'] + ip_id = register_response['ip_id'] with pytest.raises(ValueError) as exc_info: - story_client.IPAccount.transferERC20( + story_client.IPAccount.transfer_erc20( ip_id=ip_id, tokens=[ { diff --git a/tests/integration/test_integration_nft_client.py b/tests/integration/test_integration_nft_client.py index 02f13c6..fa5a1e8 100644 --- a/tests/integration/test_integration_nft_client.py +++ b/tests/integration/test_integration_nft_client.py @@ -113,7 +113,7 @@ def test_invalid_recipient_address(self, story_client): def test_invalid_mint_fee_values(self, story_client): """Test with invalid mint fee values""" with pytest.raises(ValueError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -127,7 +127,7 @@ def test_invalid_mint_fee_values(self, story_client): try: huge_mint_fee = 2**256 - 1 # Max uint256 value - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -145,7 +145,7 @@ def test_parameter_omission(self, story_client): """Test omitting required parameters""" with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( # name is omitted symbol="TEST", is_public_minting=True, @@ -155,7 +155,7 @@ def test_parameter_omission(self, story_client): ) with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", # symbol is omitted is_public_minting=True, @@ -165,7 +165,7 @@ def test_parameter_omission(self, story_client): ) with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", # is_public_minting is omitted @@ -175,7 +175,7 @@ def test_parameter_omission(self, story_client): ) with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -185,7 +185,7 @@ def test_parameter_omission(self, story_client): ) with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -195,7 +195,7 @@ def test_parameter_omission(self, story_client): ) with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.createNFTCollection( + story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -209,7 +209,7 @@ def test_authorization_errors(self, story_client): different_owner = "0x1234567890123456789012345678901234567890" - response = story_client.NFTClient.createNFTCollection( + response = story_client.NFTClient.create_nft_collection( name="test-collection", symbol="TEST", is_public_minting=True, @@ -220,8 +220,8 @@ def test_authorization_errors(self, story_client): ) assert response is not None - assert 'nftContract' in response - assert Web3.is_address(response['nftContract']) + assert 'nft_contract' in response + assert Web3.is_address(response['nft_contract']) class TestMintFee: """Tests for mint fee functionality in NFT collections""" From e092bdc0edbe738925bb13bcd9e45584a4befb30 Mon Sep 17 00:00:00 2001 From: bpolania Date: Tue, 22 Apr 2025 23:32:51 -0700 Subject: [PATCH 21/26] Update test_integration_ip_account.py --- .../test_integration_ip_account.py | 124 +----------------- 1 file changed, 6 insertions(+), 118 deletions(-) diff --git a/tests/integration/test_integration_ip_account.py b/tests/integration/test_integration_ip_account.py index 9f0c42d..0a012ba 100644 --- a/tests/integration/test_integration_ip_account.py +++ b/tests/integration/test_integration_ip_account.py @@ -106,14 +106,14 @@ def test_execute_with_sig(self, story_client): token_id=token_id ) - ip_id = response['ip_id'] + ip_id = response['ipId'] deadline = get_block_timestamp(web3) + 100 state = story_client.IPAccount.get_ip_account_nonce(ip_id) core_data = story_client.IPAccount.access_controller_client.contract.encode_abi( abi_element_identifier="setTransientPermission", args=[ - ip_id, + ipId, account.address, "0x6E81a25C99C6e8430aeC7353325EB138aFE5DC16", Web3.keccak(text="function setAll(address,string,bytes32,bytes32)")[:4], @@ -141,7 +141,7 @@ def test_execute_with_sig(self, story_client): "name": "Story Protocol IP Account", "version": "1", "chainId": 1315, - "verifyingContract": ip_id, + "verifyingContract": ipId, } message_types = { @@ -168,7 +168,7 @@ def test_execute_with_sig(self, story_client): response = story_client.IPAccount.execute_with_sig( to=story_client.IPAccount.access_controller_client.contract.address, value=0, - ip_id=ip_id, + ip_id=ipId, data=core_data, signer=account.address, deadline=deadline, @@ -319,78 +319,6 @@ def test_execute_unregistered_ip(self, story_client): assert "is not registered" in str(exc_info.value) - def test_execute_with_sig_wrong_signer(self, story_client): - """Test executeWithSig with a valid signature but wrong signer address.""" - token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - register_response = story_client.IPAsset.register( - nft_contract=MockERC721, - token_id=token_id - ) - ip_id = register_response['ip_id'] - - deadline = get_block_timestamp(web3) + 100 - state = story_client.IPAccount.get_ip_account_nonce(ip_id) - data = "0x" - - execute_data = story_client.IPAccount.ip_account_client.contract.encode_abi( - abi_element_identifier="execute", - args=[ - story_client.IPAccount.access_controller_client.contract.address, - 0, - data - ] - ) - - expected_state = Web3.keccak( - encode( - ["bytes32", "bytes"], - [state, Web3.to_bytes(hexstr=execute_data)] - ) - ) - - domain_data = { - "name": "Story Protocol IP Account", - "version": "1", - "chainId": 1315, - "verifyingContract": ip_id, - } - - message_types = { - "Execute": [ - {"name": "to", "type": "address"}, - {"name": "value", "type": "uint256"}, - {"name": "data", "type": "bytes"}, - {"name": "nonce", "type": "bytes32"}, - {"name": "deadline", "type": "uint256"}, - ], - } - - message_data = { - "to": story_client.IPAccount.access_controller_client.contract.address, - "value": 0, - "data": data, - "nonce": expected_state, - "deadline": deadline, - } - - signable_message = encode_typed_data(domain_data, message_types, message_data) - signed_message = Account.sign_message(signable_message, private_key) - wrong_signer = "0x1234567890123456789012345678901234567890" - - with pytest.raises(Exception) as exc_info: - story_client.IPAccount.execute_with_sig( - ip_id=ip_id, - to=story_client.IPAccount.access_controller_client.contract.address, - value=0, - data=data, - signer=wrong_signer, # Wrong signer address - deadline=deadline, - signature=signed_message.signature - ) - - error_hex = '0x3fd60002' - assert error_hex in str(exc_info.value), f"Expected error code {error_hex} for wrong signer" - class TestSetIpMetadata: """Tests for setting IP metadata""" @@ -484,46 +412,6 @@ def test_execute_with_sig_wrong_signer(self, story_client): error_hex = '0x3fd60002' assert error_hex in str(exc_info.value), f"Expected error code {error_hex} for wrong signer" - - @pytest.mark.skip(reason="contract allows empty calls") - def test_transfer_erc20_empty_tokens(self, story_client): - """Test transferERC20 with empty tokens list.""" - token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - register_response = story_client.IPAsset.register( - nft_contract=MockERC721, - token_id=token_id - ) - ip_id = register_response['ipId'] - - # Try to transfer with empty tokens list - with pytest.raises(Exception) as exc_info: - story_client.IPAccount.transferERC20( - ip_id=ip_id, - tokens=[] # Empty tokens list - ) - - def test_transfer_erc20_invalid_token_params(self, story_client): - """Test transferERC20 with invalid token parameters.""" - token_id = get_token_id(MockERC721, story_client.web3, story_client.account) - register_response = story_client.IPAsset.register( - nft_contract=MockERC721, - token_id=token_id - ) - ip_id = register_response['ip_id'] - - with pytest.raises(ValueError) as exc_info: - story_client.IPAccount.transfer_erc20( - ip_id=ip_id, - tokens=[ - { - # Missing 'address' - "target": story_client.account.address, - "amount": 1000000 - } - ] - ) - assert "must include" in str(exc_info.value), "Error should mention missing parameter" - class TestTransferERC20: """Tests for transferring ERC20 tokens""" @@ -537,7 +425,7 @@ def test_transfer_erc20(self, story_client): ) ip_id = response['ip_id'] - # 1. Query token balance of ip_id and wallet before + # 1. Query token balance of ipId and wallet before initial_erc20_balance_of_ip_id = story_client.Royalty.mock_erc20_client.balanceOf( account=ip_id ) @@ -595,7 +483,7 @@ def test_transfer_erc20(self, story_client): ] ) - # 5. Query token balance of ip_id and wallet address after transfer + # 5. Query token balance of ipId and wallet address after transfer final_erc20_balance_of_ip_id = story_client.Royalty.mock_erc20_client.balanceOf( account=ip_id ) From fe9a805b33753b8449b71a055770b824813a1a91 Mon Sep 17 00:00:00 2001 From: bpolania Date: Tue, 22 Apr 2025 23:35:02 -0700 Subject: [PATCH 22/26] Update test_integration_nft_client.py --- .../test_integration_nft_client.py | 113 ------------------ 1 file changed, 113 deletions(-) diff --git a/tests/integration/test_integration_nft_client.py b/tests/integration/test_integration_nft_client.py index fa5a1e8..b069589 100644 --- a/tests/integration/test_integration_nft_client.py +++ b/tests/integration/test_integration_nft_client.py @@ -110,119 +110,6 @@ def test_invalid_recipient_address(self, story_client): ) assert "when sending a str, it must be a hex string. got: '0xinvalid'" in str(exc_info.value).lower() - def test_invalid_mint_fee_values(self, story_client): - """Test with invalid mint fee values""" - with pytest.raises(ValueError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address, - mint_fee=-100, # Negative mint fee - mint_fee_token=MockERC20 - ) - assert "Invalid mint fee" in str(exc_info.value) - - try: - huge_mint_fee = 2**256 - 1 # Max uint256 value - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address, - mint_fee=huge_mint_fee, - mint_fee_token=MockERC20 - ) - - except Exception as e: - assert "overflow" in str(e).lower() or "revert" in str(e).lower() or "invalid" in str(e).lower() - - def test_parameter_omission(self, story_client): - """Test omitting required parameters""" - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - # name is omitted - symbol="TEST", - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address - ) - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - # symbol is omitted - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address - ) - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - # is_public_minting is omitted - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address - ) - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - # mint_open is omitted - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address - ) - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - mint_open=True, - # contract_uri is omitted - mint_fee_recipient=story_client.account.address - ) - - with pytest.raises(TypeError) as exc_info: - story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - mint_open=True, - contract_uri="test-uri" - # mint_fee_recipient is omitted - ) - - def test_authorization_errors(self, story_client): - """Test unauthorized operations""" - - different_owner = "0x1234567890123456789012345678901234567890" - - response = story_client.NFTClient.create_nft_collection( - name="test-collection", - symbol="TEST", - is_public_minting=True, - mint_open=True, - contract_uri="test-uri", - mint_fee_recipient=story_client.account.address, - owner=different_owner - ) - - assert response is not None - assert 'nft_contract' in response - assert Web3.is_address(response['nft_contract']) - class TestMintFee: """Tests for mint fee functionality in NFT collections""" From 42ca46bd3cb898c6355e604d5479ad1705a923b4 Mon Sep 17 00:00:00 2001 From: bpolania Date: Tue, 22 Apr 2025 23:39:03 -0700 Subject: [PATCH 23/26] Update test_integration_ip_account.py --- tests/integration/test_integration_ip_account.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_integration_ip_account.py b/tests/integration/test_integration_ip_account.py index 0a012ba..2287b7b 100644 --- a/tests/integration/test_integration_ip_account.py +++ b/tests/integration/test_integration_ip_account.py @@ -106,14 +106,14 @@ def test_execute_with_sig(self, story_client): token_id=token_id ) - ip_id = response['ipId'] + ip_id = response['ip_id'] deadline = get_block_timestamp(web3) + 100 state = story_client.IPAccount.get_ip_account_nonce(ip_id) core_data = story_client.IPAccount.access_controller_client.contract.encode_abi( abi_element_identifier="setTransientPermission", args=[ - ipId, + ip_id, account.address, "0x6E81a25C99C6e8430aeC7353325EB138aFE5DC16", Web3.keccak(text="function setAll(address,string,bytes32,bytes32)")[:4], @@ -141,7 +141,7 @@ def test_execute_with_sig(self, story_client): "name": "Story Protocol IP Account", "version": "1", "chainId": 1315, - "verifyingContract": ipId, + "verifyingContract": ip_id, } message_types = { @@ -168,7 +168,7 @@ def test_execute_with_sig(self, story_client): response = story_client.IPAccount.execute_with_sig( to=story_client.IPAccount.access_controller_client.contract.address, value=0, - ip_id=ipId, + ip_id=ip_id, data=core_data, signer=account.address, deadline=deadline, From 33a1e5fa0afb1603b048c2a8652c3611d7d4d8ae Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 24 Apr 2025 13:06:34 +0900 Subject: [PATCH 24/26] Addressed self.web3 vs Web3 cmnt --- src/story_protocol_python_sdk/resources/Group.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index b64626c..7465fa8 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -50,7 +50,7 @@ def register_group(self, group_pool: str, tx_options: dict = None) -> dict: :return dict: A dictionary with the transaction hash and group ID. """ try: - if not Web3.is_address(group_pool): + if not self.web3.is_address(group_pool): raise ValueError(f'Address "{group_pool}" is invalid.') response = build_and_send_transaction( @@ -139,10 +139,10 @@ def mint_and_register_ip_and_attach_license_and_add_to_group( :return dict: A dictionary with the transaction hash, IP ID, and token ID. """ try: - if not Web3.is_address(group_id): + if not self.web3.is_address(group_id): raise ValueError(f'Group ID "{group_id}" is invalid.') - if not Web3.is_address(spg_nft_contract): + if not self.web3.is_address(spg_nft_contract): raise ValueError(f'SPG NFT contract address "{spg_nft_contract}" is invalid.') is_registered = self.ip_asset_registry_client.isRegistered(group_id) @@ -504,7 +504,7 @@ def _get_license_data(self, license_data: list) -> list: else: license_template = self.pi_license_template_client.contract.address - if not Web3.is_address(license_template): + if not self.web3.is_address(license_template): raise ValueError(f'License template address "{license_template}" is invalid.') # Validate licensing config @@ -567,6 +567,7 @@ def _parse_tx_ip_group_registered_event(self, tx_receipt: dict) -> str: :param tx_receipt dict: The transaction receipt. :return str: The group ID. + :raises ValueError: If the event is not found in the transaction receipt. """ event_signature = self.web3.keccak(text="IPGroupRegistered(address,address)").hex() @@ -575,7 +576,7 @@ def _parse_tx_ip_group_registered_event(self, tx_receipt: dict) -> str: group_id = '0x' + log['topics'][1].hex()[24:] return self.web3.to_checksum_address(group_id) - return None + raise ValueError("IPGroupRegistered event not found in transaction receipt") def _parse_tx_ip_registered_event(self, tx_receipt: dict) -> dict: """ From 2334fe765e8480e248988ca288757cdab0c63fa6 Mon Sep 17 00:00:00 2001 From: Andrew Chung Date: Thu, 24 Apr 2025 13:07:32 +0900 Subject: [PATCH 25/26] Updated parse fns to raise error instead of returning none --- src/story_protocol_python_sdk/resources/Group.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/story_protocol_python_sdk/resources/Group.py b/src/story_protocol_python_sdk/resources/Group.py index 7465fa8..77aef26 100644 --- a/src/story_protocol_python_sdk/resources/Group.py +++ b/src/story_protocol_python_sdk/resources/Group.py @@ -584,6 +584,7 @@ def _parse_tx_ip_registered_event(self, tx_receipt: dict) -> dict: :param tx_receipt dict: The transaction receipt. :return dict: The IP ID and token ID. + :raises ValueError: If the event is not found in the transaction receipt. """ event_signature = self.web3.keccak( text="IPRegistered(address,uint256,address,uint256,string,string,uint256)" @@ -599,7 +600,7 @@ def _parse_tx_ip_registered_event(self, tx_receipt: dict) -> dict: 'token_id': token_id } - return None + raise ValueError("IPRegistered event not found in transaction receipt") def _parse_tx_collected_royalties_to_group_pool_event(self, tx_receipt: dict) -> list: """ From 8940f54750d24904b59657bf4363eea339dee13d Mon Sep 17 00:00:00 2001 From: Boris Polania Date: Wed, 23 Apr 2025 22:07:48 -0700 Subject: [PATCH 26/26] fix CORE_METADATA_MODULE issue (#71) --- tests/integration/setup_for_integration.py | 3 ++- tests/integration/utils.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration/setup_for_integration.py b/tests/integration/setup_for_integration.py index aca15c5..d77ff90 100644 --- a/tests/integration/setup_for_integration.py +++ b/tests/integration/setup_for_integration.py @@ -29,7 +29,8 @@ WIP_TOKEN_ADDRESS, setup_royalty_vault, EVEN_SPLIT_GROUP_POOL, - ROYALTY_POLICY_LRP + ROYALTY_POLICY_LRP, + CORE_METADATA_MODULE ) # Load environment variables diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 3cda671..144590a 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -21,6 +21,7 @@ ARBITRATION_POLICY_UMA="0xfFD98c3877B8789124f02C7E8239A4b0Ef11E936" EVEN_SPLIT_GROUP_POOL="0xf96f2c30b41Cb6e0290de43C8528ae83d4f33F89" ROYALTY_POLICY_LRP="0x9156e603C949481883B1d3355c6f1132D191fC41" +CORE_METADATA_MODULE="0x6E81a25C99C6e8430aeC7353325EB138aFE5DC16" def get_story_client_in_sepolia(web3: Web3, account) -> StoryClient: chain_id = 11155111 # Sepolia chain ID