Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 93 additions & 3 deletions src/story_protocol_python_sdk/resources/IPAsset.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from story_protocol_python_sdk.abi.DerivativeWorkflows.DerivativeWorkflows_client import (
DerivativeWorkflowsClient,
)
from story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client import (
IPAccountImplClient,
)
from story_protocol_python_sdk.abi.IPAssetRegistry.IPAssetRegistry_client import (
IPAssetRegistryClient,
)
Expand All @@ -35,7 +38,10 @@
)
from story_protocol_python_sdk.abi.SPGNFTImpl.SPGNFTImpl_client import SPGNFTImplClient
from story_protocol_python_sdk.types.common import AccessPermission
from story_protocol_python_sdk.types.resource.IPAsset import RegistrationResponse
from story_protocol_python_sdk.types.resource.IPAsset import (
RegisterPILTermsAndAttachResponse,
RegistrationResponse,
)
from story_protocol_python_sdk.utils.constants import (
MAX_ROYALTY_TOKEN,
ZERO_ADDRESS,
Expand Down Expand Up @@ -881,6 +887,90 @@ def mint_and_register_ip_and_make_derivative_with_license_tokens(
except Exception as e:
raise e

def register_pil_terms_and_attach(
self,
ip_id: Address,
license_terms_data: list,
deadline: int | None = None,
tx_options: dict | None = None,
) -> RegisterPILTermsAndAttachResponse:
"""
Register Programmable IP License Terms (if unregistered) and attach it to IP.

:param ip_id Address: The IP ID.
:param license_terms_data list: The data of the license and its configuration to be attached to the IP.
:param deadline int: [Optional] Signature deadline in milliseconds. If not provided, the current time + 1000ms will be used.
:param tx_options dict: [Optional] Transaction options.
:return RegisterPILTermsAndAttachResponse: Dictionary with the tx hash and license terms IDs.
"""
try:
if not self._is_registered(ip_id):
raise ValueError(f"The IP with id {ip_id} is not registered.")
calculated_deadline = self.sign_util.get_deadline(deadline=deadline)
ip_account_impl_client = IPAccountImplClient(self.web3, ip_id)
state = ip_account_impl_client.state()
license_terms = []
for term in license_terms_data:
license_terms.append(
{
"terms": self.license_terms_util.validate_license_terms(
term["terms"]
),
"licensingConfig": self.license_terms_util.validate_licensing_config(
term["licensing_config"]
),
}
)
signature_response = self.sign_util.get_permission_signature(
ip_id=ip_id,
deadline=calculated_deadline,
state=state,
permissions=[
{
"ipId": ip_id,
"signer": self.license_attachment_workflows_client.contract.address,
"to": self.licensing_module_client.contract.address,
"permission": AccessPermission.ALLOW,
"func": get_function_signature(
self.licensing_module_client.contract.abi,
"attachLicenseTerms",
),
},
{
"ipId": ip_id,
"signer": self.license_attachment_workflows_client.contract.address,
"to": self.licensing_module_client.contract.address,
"permission": AccessPermission.ALLOW,
"func": get_function_signature(
self.licensing_module_client.contract.abi,
"setLicensingConfig",
),
},
],
)
response = build_and_send_transaction(
self.web3,
self.account,
self.license_attachment_workflows_client.build_registerPILTermsAndAttach_transaction,
ip_id,
license_terms,
{
"signer": self.web3.to_checksum_address(self.account.address),
"deadline": calculated_deadline,
"signature": signature_response["signature"],
},
tx_options=tx_options,
)
license_terms_ids = self._parse_tx_license_terms_attached_event(
response["tx_receipt"]
)
return RegisterPILTermsAndAttachResponse(
tx_hash=response["tx_hash"],
license_terms_ids=license_terms_ids,
)
except Exception as e:
raise e

def _validate_derivative_data(self, derivative_data: dict) -> dict:
"""
Validates the derivative data and returns processed internal data.
Expand Down Expand Up @@ -1034,7 +1124,7 @@ def _parse_tx_license_term_attached_event(self, tx_receipt: dict) -> int | None:
return license_terms_id
return None

def _parse_tx_license_terms_attached_event(self, tx_receipt: dict) -> list | None:
def _parse_tx_license_terms_attached_event(self, tx_receipt: dict) -> list[int]:
"""
Parse the LicenseTermsAttached events from a transaction receipt.

Expand All @@ -1052,4 +1142,4 @@ def _parse_tx_license_terms_attached_event(self, tx_receipt: dict) -> list | Non
license_terms_id = int.from_bytes(data[-32:], byteorder="big")
license_terms_ids.append(license_terms_id)

return license_terms_ids if license_terms_ids else None
return license_terms_ids
13 changes: 13 additions & 0 deletions src/story_protocol_python_sdk/types/resource/IPAsset.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,16 @@ class RegistrationResponse(TypedDict):
ip_id: Address
tx_hash: HexStr
token_id: Optional[int]


class RegisterPILTermsAndAttachResponse(TypedDict):
"""
Response structure for Programmable IP License Terms registration and attachment operations.

Attributes:
tx_hash: The transaction hash of the registration transaction
license_terms_ids: The IDs of the registered license terms
"""

tx_hash: HexStr
license_terms_ids: list[int]
81 changes: 80 additions & 1 deletion tests/integration/test_integration_ip_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,88 @@ def test_with_custom_value(
nft_metadata_hash=web3.keccak(text="custom-value-metadata"),
),
recipient=account_2.address,
allow_duplicates=False,
allow_duplicates=True,
)
assert response is not None
assert isinstance(response["tx_hash"], str)
assert isinstance(response["ip_id"], str)
assert isinstance(response["token_id"], int)


class TestRegisterPilTermsAndAttach:
def test_successful_registration(
self,
story_client: StoryClient,
parent_ip_and_license_terms,
):
response = story_client.IPAsset.register_pil_terms_and_attach(
ip_id=parent_ip_and_license_terms["parent_ip_id"],
license_terms_data=[
{
"terms": {
"transferable": True,
"royalty_policy": ROYALTY_POLICY,
"default_minting_fee": 1,
"expiration": 0,
"commercial_use": True,
"commercial_attribution": False,
"commercializer_checker": ZERO_ADDRESS,
"commercializer_checker_data": ZERO_ADDRESS,
"commercial_rev_share": 90,
"commercial_rev_ceiling": 0,
"derivatives_allowed": True,
"derivatives_attribution": True,
"derivatives_approval": False,
"derivatives_reciprocal": True,
"derivative_rev_ceiling": 0,
"currency": MockERC20,
"uri": "",
},
"licensing_config": {
"is_set": True,
"minting_fee": 1,
"hook_data": "",
"licensing_hook": ZERO_ADDRESS,
"commercial_rev_share": 90,
"disabled": False,
"expect_minimum_group_reward_share": 0,
"expect_group_reward_pool": ZERO_ADDRESS,
},
},
{
"terms": {
"transferable": True,
"royalty_policy": ROYALTY_POLICY,
"default_minting_fee": 10,
"expiration": 0,
"commercial_use": True,
"commercial_attribution": False,
"commercializer_checker": ZERO_ADDRESS,
"commercializer_checker_data": ZERO_ADDRESS,
"commercial_rev_share": 10,
"commercial_rev_ceiling": 0,
"derivatives_allowed": True,
"derivatives_attribution": True,
"derivatives_approval": False,
"derivatives_reciprocal": True,
"derivative_rev_ceiling": 0,
"currency": MockERC20,
"uri": "",
},
"licensing_config": {
"is_set": False,
"minting_fee": 1,
"hook_data": "",
"licensing_hook": ZERO_ADDRESS,
"commercial_rev_share": 90,
"disabled": False,
"expect_minimum_group_reward_share": 0,
"expect_group_reward_pool": ZERO_ADDRESS,
},
},
],
deadline=10000,
)
assert response is not None
assert isinstance(response["tx_hash"], str)
assert len(response["license_terms_ids"]) == 2
2 changes: 1 addition & 1 deletion tests/integration/test_integration_wip.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class TestWIPDeposit:
def test_deposit(self, story_client: StoryClient):
"""Test depositing IP to WIP"""
ip_amt = web3.to_wei(1, "ether") # or Web3.to_wei("0.01", 'ether')
ip_amt = web3.to_wei(0.000001, "ether")

# Get balances before deposit
balance_before = story_client.get_wallet_balance()
Expand Down
1 change: 0 additions & 1 deletion tests/unit/fixtures/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"derivative_rev_ceiling": 100,
"uri": "https://example.com",
"transferable": True,
"expect_minimum_group_reward_share": 10,
}

LICENSING_CONFIG = {
Expand Down
Loading
Loading