Skip to content
Merged
2 changes: 2 additions & 0 deletions src/story_protocol_python_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from .utils.derivative_data import DerivativeDataInput
from .utils.ip_metadata import IPMetadataInput
from .utils.licensing_config_data import LicensingConfig

__all__ = [
"StoryClient",
Expand All @@ -45,6 +46,7 @@
"ClaimRewardsResponse",
"ClaimReward",
"CollectRoyaltiesResponse",
"LicensingConfig",
"RegisterPILTermsAndAttachResponse",
# Constants
"ZERO_ADDRESS",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ def __init__(self, web3: Web3):
def exists(self, licenseTemplate, licenseTermsId):
return self.contract.functions.exists(licenseTemplate, licenseTermsId).call()

def getLicensingConfig(self, ipId, licenseTemplate, licenseTermsId):
return self.contract.functions.getLicensingConfig(
ipId, licenseTemplate, licenseTermsId
).call()

def getRoyaltyPercent(self, ipId, licenseTemplate, licenseTermsId):
return self.contract.functions.getRoyaltyPercent(
ipId, licenseTemplate, licenseTermsId
Expand Down
144 changes: 64 additions & 80 deletions src/story_protocol_python_sdk/resources/License.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# src/story_protcol_python_sdk/resources/License.py

from ens.ens import HexStr
from ens.ens import Address, HexStr
from web3 import Web3

from story_protocol_python_sdk.abi.IPAssetRegistry.IPAssetRegistry_client import (
Expand All @@ -21,8 +19,15 @@
from story_protocol_python_sdk.types.common import RevShareType
from story_protocol_python_sdk.utils.constants import ZERO_ADDRESS
from story_protocol_python_sdk.utils.license_terms import LicenseTerms
from story_protocol_python_sdk.utils.licensing_config_data import (
LicensingConfig,
LicensingConfigData,
)
from story_protocol_python_sdk.utils.transaction_utils import build_and_send_transaction
from story_protocol_python_sdk.utils.validation import get_revenue_share
from story_protocol_python_sdk.utils.validation import (
get_revenue_share,
validate_address,
)


class License:
Expand Down Expand Up @@ -183,7 +188,7 @@ def register_commercial_use_pil(
currency: str,
royalty_policy: str | None = None,
tx_options: dict | None = None,
) -> dict | None:
) -> dict:
"""
Convenient function to register a PIL commercial use license to the registry.

Expand Down Expand Up @@ -214,14 +219,14 @@ def register_commercial_use_pil(
complete_license_terms,
tx_options=tx_options,
)

if not response["tx_receipt"].logs:
return None
tx_hash = response["tx_hash"]
if not response["tx_receipt"]["logs"]:
return {"tx_hash": tx_hash}

target_logs = self._parse_tx_license_terms_registered_event(
response["tx_receipt"]
)
return {"tx_hash": response["tx_hash"], "license_terms_id": target_logs}
return {"tx_hash": tx_hash, "license_terms_id": target_logs}

except Exception as e:
raise e
Expand All @@ -233,7 +238,7 @@ def register_commercial_remix_pil(
commercial_rev_share: int,
royalty_policy: str,
tx_options: dict | None = None,
) -> dict | None:
) -> dict:
"""
Convenient function to register a PIL commercial remix license to the registry.

Expand Down Expand Up @@ -267,13 +272,14 @@ def register_commercial_remix_pil(
tx_options=tx_options,
)

if not response["tx_receipt"].logs:
return None
tx_hash = response["tx_hash"]
if not response["tx_receipt"]["logs"]:
return {"tx_hash": tx_hash}

target_logs = self._parse_tx_license_terms_registered_event(
response["tx_receipt"]
)
return {"tx_hash": response["tx_hash"], "license_terms_id": target_logs}
return {"tx_hash": tx_hash, "license_terms_id": target_logs}

except Exception as e:
raise e
Expand Down Expand Up @@ -375,12 +381,8 @@ def mint_license_tokens(
:return dict: A dictionary with the transaction hash and the license token IDs.
"""
try:
if not self.web3.is_address(license_template):
raise ValueError(f'Address "{license_template}" is invalid.')

if not self.web3.is_address(receiver):
raise ValueError(f'Address "{receiver}" is invalid.')

validate_address(license_template)
validate_address(receiver)
is_registered = self.ip_asset_registry_client.isRegistered(licensor_ip_id)
if not is_registered:
raise ValueError(
Expand Down Expand Up @@ -522,7 +524,7 @@ def set_licensing_config(
self,
ip_id: str,
license_terms_id: int,
licensing_config: dict,
licensing_config: LicensingConfig,
license_template: str | None = None,
tx_options: dict | None = None,
) -> dict:
Expand All @@ -531,86 +533,36 @@ def set_licensing_config(

:param ip_id str: The address of the IP for which the configuration is being set.
:param license_terms_id int: The ID of the license terms within the license template.
:param licensing_config dict: The licensing configuration for the license.
:param isSet bool: Whether the configuration is set or not.
:param mintingFee int: The minting fee to be paid when minting license tokens.
:param hookData str: The data to be used by the licensing hook.
:param licensingHook str: The hook contract address for the licensing module, or address(0) if none.
:param commercialRevShare int: The commercial revenue share percentage.
:param disabled bool: Whether the license is disabled or not.
:param expectMinimumGroupRewardShare int: The minimum percentage of the group's reward share (0-100%, as 100 * 10^6).
:param expectGroupRewardPool str: The address of the expected group reward pool.
:param licensing_config `LicensingConfig`: The licensing configuration for the license.
:param license_template str: [Optional] The address of the license template used. If not specified, config applies to all licenses.
:param tx_options dict: [Optional] Transaction options.
:return dict: A dictionary containing the transaction hash and success status.
"""
try:
# Input validation
required_params = {
"isSet",
"mintingFee",
"hookData",
"licensingHook",
"commercialRevShare",
"disabled",
"expectMinimumGroupRewardShare",
"expectGroupRewardPool",
}

# Check for missing parameters
missing_params = required_params - set(licensing_config.keys())
if missing_params:
raise ValueError(
f"Missing required licensing_config parameters: {', '.join(missing_params)}. "
f"All parameters must be explicitly provided."
)

licensing_config["commercialRevShare"] = (
self.license_terms_util.get_revenue_share(
licensing_config["commercialRevShare"]
)
validated_licensing_config = LicensingConfigData.validate_license_config(
self.module_registry_client, licensing_config
)

if licensing_config["mintingFee"] < 0:
raise ValueError("The minting fee must be greater than 0.")

if not license_template:
license_template = ZERO_ADDRESS
if license_template is None:
license_template = self.license_template_client.contract.address
else:
validate_address(license_template)

if (
license_template == ZERO_ADDRESS
and licensing_config["commercialRevShare"] != 0
and validated_licensing_config["commercialRevShare"] != 0
):
raise ValueError(
"The license template cannot be zero address if commercial revenue share is not zero."
)

# Convert addresses to checksum format
ip_id = self.web3.to_checksum_address(ip_id)
if license_template:
license_template = self.web3.to_checksum_address(license_template)
licensing_config["licensingHook"] = self.web3.to_checksum_address(
licensing_config["licensingHook"]
)
licensing_config["expectGroupRewardPool"] = self.web3.to_checksum_address(
licensing_config["expectGroupRewardPool"]
)

# Check if IP is registered
if not self.ip_asset_registry_client.isRegistered(ip_id):
if not self.ip_asset_registry_client.isRegistered(validate_address(ip_id)):
raise ValueError(f"The licensor IP with id {ip_id} is not registered.")

# Check if license terms exist
if not self.license_template_client.exists(license_terms_id):
raise ValueError(f"License terms id {license_terms_id} does not exist.")

# Check if licensing hook is registered if provided
if licensing_config["licensingHook"] != ZERO_ADDRESS:
if not self.module_registry_client.isRegistered(
licensing_config["licensingHook"]
):
raise ValueError("The licensing hook is not registered.")

if license_template == ZERO_ADDRESS and license_terms_id != 0:
raise ValueError(
"The license template is zero address but license terms id is not zero."
Expand All @@ -623,7 +575,7 @@ def set_licensing_config(
ip_id,
license_template,
license_terms_id,
licensing_config,
validated_licensing_config,
tx_options=tx_options,
)

Expand All @@ -634,3 +586,35 @@ def set_licensing_config(

except Exception as e:
raise ValueError(f"Failed to set licensing config: {str(e)}")

def get_licensing_config(
self,
ip_id: Address,
license_terms_id: int,
license_template: Address | None = None,
) -> LicensingConfig:
"""
Gets the licensing configuration for a specific license terms of an IP.

:param ip_id Address: The address of the IP for which the configuration is being retrieved.
:param license_terms_id int: The ID of the license terms within the license template.
:param license_template Address: [Optional] The address of the license template.
Defaults to visit https://docs.story.foundation/docs/programmable-ip-license for more information if not provided.
:return LicensingConfig: A dictionary containing the licensing configuration.
"""
try:
validate_address(ip_id)

if license_template is None:
license_template = self.license_template_client.contract.address
else:
validate_address(license_template)

licensing_config = self.license_registry_client.getLicensingConfig(
ip_id, license_template, license_terms_id
)

return LicensingConfigData.from_tuple(licensing_config)

except Exception as e:
raise ValueError(f"Failed to get licensing config: {str(e)}")
7 changes: 6 additions & 1 deletion src/story_protocol_python_sdk/scripts/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,12 @@
{
"contract_name": "LicenseRegistry",
"contract_address": "0x529a750E02d8E2f15649c13D69a465286a780e24",
"functions": ["exists", "hasIpAttachedLicenseTerms", "getRoyaltyPercent"]
"functions": [
"exists",
"hasIpAttachedLicenseTerms",
"getRoyaltyPercent",
"getLicensingConfig"
]
},
{
"contract_name": "RoyaltyPolicyLRP",
Expand Down
Loading
Loading