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
10 changes: 9 additions & 1 deletion src/story_protocol_python_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
CollectRoyaltiesResponse,
)
from .types.resource.IPAsset import (
LicenseTermsDataInput,
RegisterAndAttachAndDistributeRoyaltyTokensResponse,
RegisterPILTermsAndAttachResponse,
RegistrationResponse,
RegistrationWithRoyaltyVaultAndLicenseTermsResponse,
RegistrationWithRoyaltyVaultResponse,
)
from .types.resource.License import LicenseTermsInput
from .types.resource.Royalty import RoyaltyShareInput
from .utils.constants import (
DEFAULT_FUNCTION_SELECTOR,
MAX_ROYALTY_TOKEN,
Expand All @@ -30,7 +35,6 @@
from .utils.derivative_data import DerivativeDataInput
from .utils.ip_metadata import IPMetadataInput
from .utils.licensing_config_data import LicensingConfig
from .utils.royalty_shares import RoyaltyShareInput

__all__ = [
"StoryClient",
Expand All @@ -46,12 +50,16 @@
"IPMetadataInput",
"RegistrationResponse",
"RegistrationWithRoyaltyVaultResponse",
"RegistrationWithRoyaltyVaultAndLicenseTermsResponse",
"RegisterAndAttachAndDistributeRoyaltyTokensResponse",
"LicenseTermsDataInput",
"ClaimRewardsResponse",
"ClaimReward",
"CollectRoyaltiesResponse",
"LicensingConfig",
"RegisterPILTermsAndAttachResponse",
"RoyaltyShareInput",
"LicenseTermsInput",
# Constants
"ZERO_ADDRESS",
"ZERO_HASH",
Expand Down
326 changes: 283 additions & 43 deletions src/story_protocol_python_sdk/resources/IPAsset.py

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions src/story_protocol_python_sdk/types/resource/IPAsset.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from dataclasses import dataclass
from typing import TypedDict

from ens.ens import Address, HexStr

from story_protocol_python_sdk.types.resource.License import LicenseTermsInput
from story_protocol_python_sdk.utils.licensing_config_data import LicensingConfig


class RegistrationResponse(TypedDict):
"""
Expand Down Expand Up @@ -31,6 +35,21 @@ class RegistrationWithRoyaltyVaultResponse(RegistrationResponse):
royalty_vault: Address


class RegistrationWithRoyaltyVaultAndLicenseTermsResponse(
RegistrationWithRoyaltyVaultResponse
):
"""
Response structure for IP asset registration operations with royalty vault and license terms.

Extends `RegistrationWithRoyaltyVaultResponse` with license terms information.

Attributes:
license_terms_ids: The IDs of the license terms attached to the IP asset.
"""

license_terms_ids: list[int]


class RegisterPILTermsAndAttachResponse(TypedDict):
"""
Response structure for Programmable IP License Terms registration and attachment operations.
Expand All @@ -42,3 +61,32 @@ class RegisterPILTermsAndAttachResponse(TypedDict):

tx_hash: HexStr
license_terms_ids: list[int]


class RegisterAndAttachAndDistributeRoyaltyTokensResponse(
RegistrationWithRoyaltyVaultAndLicenseTermsResponse
):
"""
Response structure for IP asset registration operations with royalty vault, license terms and distribute royalty tokens.

Extends `RegistrationWithRoyaltyVaultAndLicenseTermsResponse` with distribute royalty tokens transaction hash.

Attributes:
distribute_royalty_tokens_tx_hash: The transaction hash of the distribute royalty tokens transaction.
"""

distribute_royalty_tokens_tx_hash: HexStr


@dataclass
class LicenseTermsDataInput:
"""
Data structure for license terms.

Attributes:
terms: The terms of the license.
licensing_config: The licensing configuration of the license.
"""

terms: LicenseTermsInput
licensing_config: LicensingConfig
52 changes: 52 additions & 0 deletions src/story_protocol_python_sdk/types/resource/License.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from dataclasses import dataclass

from ens.ens import Address, HexStr

from story_protocol_python_sdk.types.resource.Royalty import RoyaltyPolicyInput


@dataclass
class LicenseTermsInput:
"""
This structure defines the terms for a Programmable IP License (PIL).
These terms can be attached to IP Assets.

For more information, see https://docs.story.foundation/concepts/programmable-ip-license/pil-terms

Attributes:
transferable: Indicates whether the license is transferable or not.
royalty_policy: The address of the royalty policy contract which required to StoryProtocol in advance.
default_minting_fee: The default minting fee to be paid when minting a license.
expiration: The expiration period of the license.
commercial_use: Indicates whether the work can be used commercially or not.
commercial_attribution: Whether attribution is required when reproducing the work commercially or not.
commercializer_checker: Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced.
commercializer_checker_data: The data to be passed to the commercializer checker contract.
commercial_rev_share: Percentage of revenue that must be shared with the licensor. Must be between 0 and 100 (where 100% represents 100_000_000).
commercial_rev_ceiling: The maximum revenue that can be generated from the commercial use of the work.
derivatives_allowed: Indicates whether the licensee can create derivatives of his work or not.
derivatives_attribution: Indicates whether attribution is required for derivatives of the work or not.
derivatives_approval: Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not.
derivatives_reciprocal: Indicates whether the licensee must license derivatives of the work under the same terms or not.
derivative_rev_ceiling: The maximum revenue that can be generated from the derivative use of the work.
currency: The ERC20 token to be used to pay the minting fee. The token must be registered in story protocol.
uri: The URI of the license terms, which can be used to fetch the offchain license terms.
"""

transferable: bool
royalty_policy: RoyaltyPolicyInput
default_minting_fee: int
expiration: int
commercial_use: bool
commercial_attribution: bool
commercializer_checker: Address
commercializer_checker_data: Address | HexStr
commercial_rev_share: int
commercial_rev_ceiling: int
derivatives_allowed: bool
derivatives_attribution: bool
derivatives_approval: bool
derivatives_reciprocal: bool
derivative_rev_ceiling: int
currency: Address
uri: str
40 changes: 40 additions & 0 deletions src/story_protocol_python_sdk/types/resource/Royalty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from dataclasses import dataclass
from enum import IntEnum

from ens.ens import Address


class NativeRoyaltyPolicy(IntEnum):
"""
Native royalty policy created by the Story team.

For more information:
- LAP: https://docs.story.foundation/concepts/royalty-module/liquid-absolute-percentage
- LRP: https://docs.story.foundation/concepts/royalty-module/liquid-relative-percentage

Attributes:
LAP: Liquid Absolute Percentage - defines that each parent IP Asset can choose a minimum royalty percentage that all of its downstream IP Assets in a derivative chain will share from their monetary gains as defined in the license agreement.
LRP: Liquid Relative Percentage - royalty policy defines that each parent IP Asset can choose a minimum royalty percentage that only the direct derivative IP Assets in a derivative chain will share from their monetary gains as defined in the license agreement.
"""

LAP = 0
LRP = 1


# Type alias for royalty policy input
# Allow custom royalty policy address or use a native royalty policy enum.
# For custom royalty policy, see https://docs.story.foundation/concepts/royalty-module/external-royalty-policies
RoyaltyPolicyInput = Address | NativeRoyaltyPolicy


@dataclass
class RoyaltyShareInput:
"""Input data structure for a single royalty share.

Attributes:
recipient: The address of the recipient.
percentage: The percentage of the total royalty share. Supports up to 6 decimal places precision. For example, a value of 10 represents 10% of max royalty shares, which is 10,000,000.
"""

recipient: Address
percentage: float | int
93 changes: 93 additions & 0 deletions src/story_protocol_python_sdk/utils/royalty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Module for handling royalty-related utilities including shares and policy conversions."""

from typing import List

from ens.ens import Address

from story_protocol_python_sdk.types.resource.Royalty import (
NativeRoyaltyPolicy,
RoyaltyShareInput,
)
from story_protocol_python_sdk.utils.constants import (
ROYALTY_POLICY_LAP_ADDRESS,
ROYALTY_POLICY_LRP_ADDRESS,
)
from story_protocol_python_sdk.utils.validation import validate_address


def get_royalty_shares(royalty_shares: list[RoyaltyShareInput]) -> dict:
"""
Validate and convert royalty shares.

:param royalty_shares: List of `RoyaltyShareInput`.
:return: Dictionary with validated royalty_shares and total_amount.
"""
if len(royalty_shares) == 0:
raise ValueError("Royalty shares must be provided.")

actual_total = 0
sum_percentage = 0.0
converted_shares: List[dict] = []

for share_dict in royalty_shares:
recipient = validate_address(share_dict.recipient)
percentage = share_dict.percentage

if percentage < 0:
raise ValueError(
"The percentage of the royalty shares must be greater than or equal to 0."
)

if percentage > 100:
raise ValueError(
"The percentage of the royalty shares must be less than or equal to 100."
)

sum_percentage += percentage
if sum_percentage > 100:
raise ValueError("The sum of the royalty shares cannot exceeds 100.")

value = int(percentage * 10**6)
actual_total += value

converted_shares.append(
{
"recipient": recipient,
"percentage": value,
}
)

return {"royalty_shares": converted_shares, "total_amount": actual_total}


def royalty_policy_input_to_address(
input: Address | NativeRoyaltyPolicy | None = None,
) -> Address:
"""
Convert RoyaltyPolicyInput to an address.

Args:
input: The royalty policy input. Can be None, a NativeRoyaltyPolicy enum value, or a custom address.

Returns:
Address: The corresponding royalty policy address.
- If None, returns the default LAP policy address
- If a string address, validates and returns it (custom address)
- If NativeRoyaltyPolicy.LAP (0), returns the LAP policy address
- If NativeRoyaltyPolicy.LRP (1), returns the LRP policy address

Raises:
ValueError: If the custom address is invalid.
"""
if input is None:
return ROYALTY_POLICY_LAP_ADDRESS

if isinstance(input, str):
return validate_address(input)

if input == NativeRoyaltyPolicy.LAP:
return ROYALTY_POLICY_LAP_ADDRESS
elif input == NativeRoyaltyPolicy.LRP:
return ROYALTY_POLICY_LRP_ADDRESS

return ROYALTY_POLICY_LAP_ADDRESS
71 changes: 0 additions & 71 deletions src/story_protocol_python_sdk/utils/royalty_shares.py

This file was deleted.

Loading
Loading