diff --git a/CHANGELOG.md b/CHANGELOG.md index c6d4dc504..a350fea5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) @@ -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 diff --git a/examples/custom_fee_royalty.py b/examples/custom_fee_royalty.py index 4be8982dd..4723ddd2f 100644 --- a/examples/custom_fee_royalty.py +++ b/examples/custom_fee_royalty.py @@ -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 +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: + """ + 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 + 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) ) + + # Sign and execute + submitted_tx = tx.execute(client) + receipt = submitted_tx.get_receipt(client) + 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() \ No newline at end of file + main() \ No newline at end of file