Skip to content
Open
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
- fix: Replaced `collections.namedtuple` with `typing.NamedTuple` in `client.py` for improved type checking.
- chore: Refactored examples/custom_fee.py into three separate example files.
- Expanded `docs/sdk_developers/checklist.md` with a self-review guide for all pull request submission requirements (#645).
- chore: Adapt examples/custom_fee_royalty.py to be a full end-to-end example (#679)



Expand All @@ -37,7 +38,6 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
- fix: Resolve `__eq__` type conflict in `CustomFee` class (#627)

### Breaking Changes

## [0.1.7] - 2025-10-28

### Added
Expand Down
134 changes: 107 additions & 27 deletions examples/custom_fee_royalty.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,117 @@
"""
Run with:
uv run examples/custom_royalty_fee.py
python examples/custom_royalty_fee.py
End-to-end example for creating a Non-Fungible Token (NFT)
with a custom royalty fee on the Hedera testnet.
"""
from hiero_sdk_python.tokens.custom_fixed_fee import CustomFixedFee
from hiero_sdk_python.tokens.custom_royalty_fee import CustomRoyaltyFee

import os
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're missing some key imports that you are using, for example
import sys

from dotenv import load_dotenv
from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.tokens.token_id import TokenId
from hiero_sdk_python.client.client import Client
from hiero_sdk_python.custom_fees.custom_royalty_fee import CustomRoyaltyFee
from hiero_sdk_python.token.token_create_transaction import TokenCreateTransaction
from hiero_sdk_python.token.token_info_query import TokenInfoQuery
from hiero_sdk_python.token.token_type import TokenType
from hiero_sdk_python.token.token_supply_type import TokenSupplyType
from hiero_sdk_python.key.key import Key
from hiero_sdk_python.hbar.hbar import Hbar

def set_up_client() -> Client:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def setup_client():
"""Initialize and set up the client with operator account"""
network = Network(os.getenv('NETWORK'))
client = Client(network)

operator_id = AccountId.from_string(os.getenv("OPERATOR_ID"))
operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY"))
client.set_operator(operator_id, operator_key)

return client

"""
Sets up and returns the Hedera client.
(Copied from other examples for consistency)
"""
load_dotenv()
try:
operator_id_str = os.environ["OPERATOR_ID"]
operator_key_str = os.environ["OPERATOR_KEY"]
except KeyError:
raise Exception("OPERATOR_ID and OPERATOR_KEY env variables must be set")

client = Client.for_testnet()
client.set_operator(AccountId.from_string(operator_id_str), Key.from_string(operator_key_str))
return client

def custom_royalty_fee():
fallback_fee = CustomFixedFee(
amount=50,
denominating_token_id=TokenId(0, 0, 789),
def create_token_with_fee(client: Client, fee: CustomRoyaltyFee) -> AccountId:
"""
Creates a new Non-Fungible Token (NFT) with a custom royalty fee.
Royalty fees can only be applied to NON_FUNGIBLE_UNIQUE token types.
"""
print("Creating a new NFT with a custom royalty fee...")

operator_id = client.operator_account_id
operator_key = client.operator_private_key

# Create the TokenCreateTransaction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will all fail

tx = TokenCreateTransaction(
    token_name="My Royalty NFT",
    token_symbol="MRNFT",
    token_type=TokenType.NON_FUNGIBLE_UNIQUE,
    supply_type=TokenSupplyType.FINITE,
    treasury_account_id=operator_id,
    # Add the custom fee we defined
    custom_fees=[fee],
    admin_key=operator_key,
    supply_key=operator_key,
    max_supply=100,
    # Set transaction fee
    max_transaction_fee=Hbar(30) 
)

Search for any other TokenCreateTransaction example and notice they tend to use

.set_token_name
.set_etc

That's because if you look at the code for src/hiero_sdk_python/tokens/token_create_transaction.py
you'll see it allows you to use setters

but if you instead want to pass parameters directly (like your attempt), you need to pass it in the way the class allows, which would actually be:

1. Define your token parameters

token_params = TokenParams(
token_name="My Royalty NFT",
token_symbol="MRNFT",
treasury_account_id=operator_id,
token_type=TokenType.NON_FUNGIBLE_UNIQUE,
supply_type=SupplyType.FINITE,
max_supply=100,
custom_fees=[fee],
)

2. Define your token keys

token_keys = TokenKeys(
admin_key=operator_key,
supply_key=operator_key
)

3. Create the TokenCreateTransaction

tx = TokenCreateTransaction(
token_params=token_params,
keys=token_keys
)
like this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note we don't have functionality in token create for you to create a max_transaction_fee so that won't work either

tx = TokenCreateTransaction(
token_name="My Royalty NFT",
token_symbol="MRNFT",
token_type=TokenType.NON_FUNGIBLE_UNIQUE,
supply_type=TokenSupplyType.FINITE,
treasury_account_id=operator_id,
# Add the custom fee we defined
custom_fees=[fee],
admin_key=operator_key,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do not need an admin key, i think
admin_key=operator_key,

supply_key=operator_key,
max_supply=100,
# Set transaction fee
max_transaction_fee=Hbar(30)
)

# Sign and execute
submitted_tx = tx.execute(client)
receipt = submitted_tx.get_receipt(client)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_receipt is not something we do

we do more like this:
check out other examples for token create and you'll see this

    # .set_max_transaction_fee(Hbar(30))  # Set transaction fee
    .execute(client)
)

if receipt.status != ResponseCode.SUCCESS:
    print(f"NFT token creation failed with status: {ResponseCode(receipt.status).name}")
    sys.exit(1)

token_id = receipt.token_id
print(f"NFT token created with ID: {token_id}")
return token_id

token_id = receipt.token_id

print(f"Successfully created token with ID: {token_id}")
return token_id

def query_token(client: Client, token_id: AccountId):
"""
Queries the token info to verify the custom fee.
"""
print(f"\nQuerying token {token_id} for custom fee verification...")

token_info = TokenInfoQuery().set_token_id(token_id).execute(client)

print(f"Found {len(token_info.custom_fees)} custom fees.")

if token_info.custom_fees:
# Access the first fee (we only added one)
fee = token_info.custom_fees[0]
if isinstance(fee, CustomRoyaltyFee):
print("Verified: CustomRoyaltyFee found.")
print(f" Numerator: {fee.numerator}")
print(f" Denominator: {fee.denominator}")
print(f" Fee Collector: {fee.fee_collector_account_id}")
else:
print(f"Verified: Found a fee, but it's not a Royalty Fee. Type: {type(fee)}")
else:
print("Error: No custom fees found on the token.")

def main():
"""
Main function to orchestrate the end-to-end example.
"""
client = set_up_client()
operator_id = client.operator_account_id


# This will be a 10/100 (10%) royalty fee, paid to the operator's account
royalty_fee = CustomRoyaltyFee(
numerator=5,
numerator=10,
denominator=100,
fallback_fee=fallback_fee,
fee_collector_account_id=AccountId(0, 0, 456),
all_collectors_are_exempt=True,
fallback_fee=None, # No fallback fee for this example
fee_collector_account_id=operator_id
)
print("\nCustomRoyaltyFee:")
print(f"Numerator: {royalty_fee.numerator}")
print(f"Denominator: {royalty_fee.denominator}")
print(f"Fallback Fee Amount: {royalty_fee.fallback_fee.amount if royalty_fee.fallback_fee is not None else 'None'}")
print(f"Fallback Fee Denominating Token ID: {royalty_fee.fallback_fee.denominating_token_id if royalty_fee.fallback_fee is not None else 'None'}")
print(f"Fee Collector Account ID: {royalty_fee.fee_collector_account_id}")
print(f"All Collectors Exempt: {royalty_fee.all_collectors_are_exempt}")

# Convert to protobuf
royalty_fee_proto = royalty_fee._to_proto()

print("Royalty Fee Protobuf:", royalty_fee_proto)

try:

token_id = create_token_with_fee(client, royalty_fee)


query_token(client, token_id)
except Exception as e:
print(f"\nAn error occurred: {e}")

if __name__ == "__main__":
custom_royalty_fee()
main()