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
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,44 @@ def build_registerIpAndMakeDerivative_transaction(
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)
101 changes: 101 additions & 0 deletions src/story_protocol_python_sdk/resources/IPAsset.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,107 @@ def mint_and_register_ip_and_make_derivative_with_license_tokens(
except Exception as e:
raise e

def register_ip_and_make_derivative_with_license_tokens(
self,
nft_contract: str,
token_id: int,
license_token_ids: list[int],
max_rts: int = 100_000_000,
deadline: int = 1000,
ip_metadata: IPMetadataInput | None = None,
tx_options: dict | None = None,
) -> RegistrationResponse:
"""
Register the given NFT as a derivative IP using license tokens.

:param nft_contract str: The address of the NFT collection.
:param token_id int: The ID of the NFT.
:param license_token_ids list[int]: The IDs of the license tokens to be burned for linking the IP to parent IPs.
:param max_rts int: [Optional] The maximum number of royalty tokens that can be distributed to the external royalty policies (max: 100,000,000). (default: 100,000,000)
:param deadline int: [Optional] Signature deadline in milliseconds. (default: 1000)
:param ip_metadata IPMetadataInput: [Optional] The desired metadata for the newly registered IP.
:param tx_options dict: [Optional] Transaction options.
:return RegistrationResponse: Dictionary with the tx hash, IP ID and token ID.
"""
try:
ip_id = self._get_ip_id(nft_contract, token_id)
if self._is_registered(ip_id):
raise ValueError(
f"The NFT with id {token_id} is already registered as IP."
)

# Validate license token IDs and ownership
validated_license_token_ids = self._validate_license_token_ids(
license_token_ids
)

# Validate max_rts
validate_max_rts(max_rts)

calculated_deadline = self.sign_util.get_deadline(deadline=deadline)

# Get permission signature for registration and derivative
signature_response = self.sign_util.get_permission_signature(
ip_id=ip_id,
deadline=calculated_deadline,
state=Web3.to_bytes(0),
permissions=[
{
"ipId": ip_id,
"signer": self.derivative_workflows_client.contract.address,
"to": self.core_metadata_module_client.contract.address,
"permission": AccessPermission.ALLOW,
"func": get_function_signature(
self.core_metadata_module_client.contract.abi,
"setAll",
),
},
{
"ipId": ip_id,
"signer": self.derivative_workflows_client.contract.address,
"to": self.licensing_module_client.contract.address,
"permission": AccessPermission.ALLOW,
"func": get_function_signature(
self.licensing_module_client.contract.abi,
"registerDerivativeWithLicenseTokens",
),
},
],
)

response = build_and_send_transaction(
self.web3,
self.account,
self.derivative_workflows_client.build_registerIpAndMakeDerivativeWithLicenseTokens_transaction,
validate_address(nft_contract),
token_id,
validated_license_token_ids,
ZERO_ADDRESS, # royaltyContext
max_rts,
IPMetadata.from_input(ip_metadata).get_validated_data(),
{
"signer": self.web3.to_checksum_address(self.account.address),
"deadline": calculated_deadline,
"signature": self.web3.to_bytes(
hexstr=signature_response["signature"]
),
},
tx_options=tx_options,
)

ip_registered = self._parse_tx_ip_registered_event(response["tx_receipt"])

return RegistrationResponse(
tx_hash=response["tx_hash"],
ip_id=ip_registered["ip_id"],
token_id=ip_registered["token_id"],
)

except Exception as e:
raise ValueError(
f"Failed to register IP and make derivative with license tokens: {str(e)}"
) from e

def register_pil_terms_and_attach(
self,
ip_id: Address,
Expand Down
3 changes: 2 additions & 1 deletion src/story_protocol_python_sdk/scripts/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@
"functions": [
"registerIpAndMakeDerivative",
"mintAndRegisterIpAndMakeDerivative",
"mintAndRegisterIpAndMakeDerivativeWithLicenseTokens"
"mintAndRegisterIpAndMakeDerivativeWithLicenseTokens",
"registerIpAndMakeDerivativeWithLicenseTokens"
]
}
]
Expand Down
114 changes: 114 additions & 0 deletions tests/integration/test_integration_ip_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,120 @@ def test_register_derivative_with_license_tokens(
assert isinstance(response["tx_hash"], str)
assert len(response["tx_hash"]) > 0

def test_register_ip_and_make_derivative_with_license_tokens(
self, story_client: StoryClient, parent_ip_id, non_commercial_license
):
"""Test registering an NFT as IP and making it derivative with license tokens."""
# Mint a new NFT that will be registered as derivative IP
token_id = get_token_id(MockERC721, story_client.web3, story_client.account)

# Mint license tokens from the parent IP
license_token_response = story_client.License.mint_license_tokens(
licensor_ip_id=parent_ip_id,
license_template=PIL_LICENSE_TEMPLATE,
license_terms_id=non_commercial_license,
amount=1,
receiver=account.address,
max_minting_fee=0,
max_revenue_share=1,
)
license_token_ids = license_token_response["license_token_ids"]

# approve license tokens
approve(
erc20_contract_address=LicenseTokenClient(
story_client.web3
).contract.address,
web3=story_client.web3,
account=account,
spender_address=DerivativeWorkflowsClient(
story_client.web3
).contract.address,
amount=license_token_ids[0],
)

response = (
story_client.IPAsset.register_ip_and_make_derivative_with_license_tokens(
nft_contract=MockERC721,
token_id=token_id,
license_token_ids=license_token_ids,
)
)

assert response is not None
assert "tx_hash" in response
assert response["tx_hash"] is not None

assert "ip_id" in response
assert response["ip_id"] is not None

assert "token_id" in response
assert response["token_id"] == token_id

def test_register_ip_and_make_derivative_with_license_tokens_with_metadata(
self, story_client: StoryClient, parent_ip_id, non_commercial_license
):
"""Test registering an NFT as IP and making it derivative with license tokens and metadata."""
# Mint a new NFT that will be registered as derivative IP
token_id = get_token_id(MockERC721, story_client.web3, story_client.account)

# Mint license tokens from the parent IP
license_token_response = story_client.License.mint_license_tokens(
licensor_ip_id=parent_ip_id,
license_template=PIL_LICENSE_TEMPLATE,
license_terms_id=non_commercial_license,
amount=1,
receiver=account.address,
max_minting_fee=0,
max_revenue_share=1,
)
license_token_ids = license_token_response["license_token_ids"]

# Create metadata for the derivative IP
metadata = IPMetadataInput(
ip_metadata_uri="https://ipfs.io/ipfs/derivative-test",
ip_metadata_hash=web3.to_hex(web3.keccak(text="derivative-metadata-hash")),
nft_metadata_uri="https://ipfs.io/ipfs/derivative-nft-test",
nft_metadata_hash=web3.to_hex(
web3.keccak(text="derivative-nft-metadata-hash")
),
)
# approve license tokens
approve(
erc20_contract_address=LicenseTokenClient(
story_client.web3
).contract.address,
web3=story_client.web3,
account=account,
spender_address=DerivativeWorkflowsClient(
story_client.web3
).contract.address,
amount=license_token_ids[0],
)
# Test the new method with metadata
response = (
story_client.IPAsset.register_ip_and_make_derivative_with_license_tokens(
nft_contract=MockERC721,
token_id=token_id,
license_token_ids=license_token_ids,
max_rts=10 * 10**6,
ip_metadata=metadata,
deadline=2000,
)
)

# Verify response structure
assert response is not None
assert "tx_hash" in response
assert response["tx_hash"] is not None

assert "ip_id" in response
assert response["ip_id"] is not None

assert "token_id" in response
assert response["token_id"] is not None
assert response["token_id"] == token_id


class TestIPAssetMinting:
@pytest.fixture(scope="module")
Expand Down
Loading
Loading