From 9f1628ba6fc243c486bd5bf8b3136920ae0d181b Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Wed, 12 Nov 2025 10:11:24 +0530 Subject: [PATCH 1/6] refactor(examples): modularize token_transfer.py for clarity and reuse Signed-off-by: Raja Rathour --- examples/token_transfer.py | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 examples/token_transfer.py diff --git a/examples/token_transfer.py b/examples/token_transfer.py new file mode 100644 index 000000000..0835ca62a --- /dev/null +++ b/examples/token_transfer.py @@ -0,0 +1,98 @@ +""" +Example: Token Transfer Transaction (Modularized) + +This example demonstrates transferring tokens (HBARs or custom tokens) +between two Hedera accounts using the Hiero Python SDK. +It has been refactored for modularity and clarity. + +Functions: +- setup_client(): Initializes and configures the Hedera client. +- transfer_transaction(): Executes a token/HBAR transfer. +- account_balance_query(): Checks the account balance post-transfer. +- main(): Orchestrates the above functions. +""" + +from hiero_sdk_python import ( + Client, + AccountId, + PrivateKey, + TransferTransaction, + AccountBalanceQuery, + Hbar, +) +import os + + +def setup_client() -> Client: + """ + Set up the Hedera client for Testnet. + + Returns: + Client: Configured Hedera client. + """ + operator_id = AccountId.fromString(os.getenv("OPERATOR_ID", "0.0.1234")) + operator_key = PrivateKey.fromString(os.getenv("OPERATOR_KEY", "302e...")) + client = Client.for_testnet() + client.set_operator(operator_id, operator_key) + return client + + +def transfer_transaction( + client: Client, sender: AccountId, receiver: AccountId, amount_tinybars: int +) -> str: + """ + Perform an HBAR transfer transaction between two accounts. + + Args: + client (Client): The active Hedera client. + sender (AccountId): The sender account ID. + receiver (AccountId): The receiver account ID. + amount_tinybars (int): Amount to transfer in tinybars. + + Returns: + str: The transaction ID. + """ + print(f"šŸš€ Initiating transfer of {amount_tinybars} tinybars from {sender} to {receiver}...") + txn = ( + TransferTransaction() + .add_hbar_transfer(sender, Hbar.fromTinybars(-amount_tinybars)) + .add_hbar_transfer(receiver, Hbar.fromTinybars(amount_tinybars)) + .execute(client) + ) + receipt = txn.get_receipt(client) + print(f"āœ… Transfer complete. Status: {receipt.status}") + return str(txn.transaction_id) + + +def account_balance_query(client: Client, account_id: AccountId) -> None: + """ + Query and print the account balance. + + Args: + client (Client): The active Hedera client. + account_id (AccountId): The account to query. + """ + balance = AccountBalanceQuery().set_account_id(account_id).execute(client) + print(f"šŸ’° Balance for {account_id}: {balance.hbars.toTinybars()} tinybars") + + +def main() -> None: + """ + Main entry point to perform token transfer and balance verification. + """ + client = setup_client() + + sender = AccountId.fromString(os.getenv("SENDER_ID", "0.0.1234")) + receiver = AccountId.fromString(os.getenv("RECEIVER_ID", "0.0.5678")) + amount_tinybars = int(os.getenv("TRANSFER_AMOUNT", "1000")) + + txn_id = transfer_transaction(client, sender, receiver, amount_tinybars) + + print(f"šŸ” Transaction ID: {txn_id}") + print("\nšŸ“Š Checking updated balances:") + account_balance_query(client, sender) + account_balance_query(client, receiver) + + +if __name__ == "__main__": + main() From 8362dd40246816fca4e2861e1f31f628044ca480 Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Wed, 12 Nov 2025 21:06:34 +0530 Subject: [PATCH 2/6] docs: add changelog entry for modular token_transfer refactor (#773) Signed-off-by: Raja Rathour --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b91a2140..1f2ba8fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Changed -- Refactored token-related example scripts (`token_delete.py`, `token_dissociate.py`, etc.) for improved readability and modularity. [#370] +- Refactored `examples/token_transfer.py` into modular functions (`account_balance_query()` and `transfer_transaction()`), improving readability, structure, and reusability. + The example now runs through a clear `main()` entry point and maintains identical output and functionality. (#773) ### Fixed From ab8dc2940a89cc580d9ad99c201e25b5d4f82ef1 Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Wed, 12 Nov 2025 21:19:40 +0530 Subject: [PATCH 3/6] docs: add changelog entry for modular token_transfer refactor (#773) Signed-off-by: Raja Rathour --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bbfb001b..79eec4b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ### Changed -- Refactored `examples/token_transfer.py` into modular functions (`account_balance_query()` and `transfer_transaction()`), improving readability, structure, and reusability. - The example now runs through a clear `main()` entry point and maintains identical output and functionality. (#773) +- Refactored token-related example scripts (`token_delete.py`, `token_dissociate.py`, etc.) for improved readability and modularity. [#370] +- upgrade: step security action upgraded from harden-runner-2.13.1 to harden-runner-2.13.1 ### Fixed - chore: fixed integration test names without a test prefix or postfix From c06dc7f904c4a4a4f98147c290e7a1f7e599c639 Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Sat, 22 Nov 2025 12:41:24 +0530 Subject: [PATCH 4/6] refactor(examples): modularize transfer_token.py (#773) Signed-off-by: Raja Rathour --- examples/token_transfer.py | 98 -------------------------------------- examples/transfer_token.py | 55 ++++++++++----------- 2 files changed, 26 insertions(+), 127 deletions(-) delete mode 100644 examples/token_transfer.py diff --git a/examples/token_transfer.py b/examples/token_transfer.py deleted file mode 100644 index 0835ca62a..000000000 --- a/examples/token_transfer.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -Example: Token Transfer Transaction (Modularized) - -This example demonstrates transferring tokens (HBARs or custom tokens) -between two Hedera accounts using the Hiero Python SDK. -It has been refactored for modularity and clarity. - -Functions: -- setup_client(): Initializes and configures the Hedera client. -- transfer_transaction(): Executes a token/HBAR transfer. -- account_balance_query(): Checks the account balance post-transfer. -- main(): Orchestrates the above functions. -""" - -from hiero_sdk_python import ( - Client, - AccountId, - PrivateKey, - TransferTransaction, - AccountBalanceQuery, - Hbar, -) -import os - - -def setup_client() -> Client: - """ - Set up the Hedera client for Testnet. - - Returns: - Client: Configured Hedera client. - """ - operator_id = AccountId.fromString(os.getenv("OPERATOR_ID", "0.0.1234")) - operator_key = PrivateKey.fromString(os.getenv("OPERATOR_KEY", "302e...")) - client = Client.for_testnet() - client.set_operator(operator_id, operator_key) - return client - - -def transfer_transaction( - client: Client, sender: AccountId, receiver: AccountId, amount_tinybars: int -) -> str: - """ - Perform an HBAR transfer transaction between two accounts. - - Args: - client (Client): The active Hedera client. - sender (AccountId): The sender account ID. - receiver (AccountId): The receiver account ID. - amount_tinybars (int): Amount to transfer in tinybars. - - Returns: - str: The transaction ID. - """ - print(f"šŸš€ Initiating transfer of {amount_tinybars} tinybars from {sender} to {receiver}...") - txn = ( - TransferTransaction() - .add_hbar_transfer(sender, Hbar.fromTinybars(-amount_tinybars)) - .add_hbar_transfer(receiver, Hbar.fromTinybars(amount_tinybars)) - .execute(client) - ) - receipt = txn.get_receipt(client) - print(f"āœ… Transfer complete. Status: {receipt.status}") - return str(txn.transaction_id) - - -def account_balance_query(client: Client, account_id: AccountId) -> None: - """ - Query and print the account balance. - - Args: - client (Client): The active Hedera client. - account_id (AccountId): The account to query. - """ - balance = AccountBalanceQuery().set_account_id(account_id).execute(client) - print(f"šŸ’° Balance for {account_id}: {balance.hbars.toTinybars()} tinybars") - - -def main() -> None: - """ - Main entry point to perform token transfer and balance verification. - """ - client = setup_client() - - sender = AccountId.fromString(os.getenv("SENDER_ID", "0.0.1234")) - receiver = AccountId.fromString(os.getenv("RECEIVER_ID", "0.0.5678")) - amount_tinybars = int(os.getenv("TRANSFER_AMOUNT", "1000")) - - txn_id = transfer_transaction(client, sender, receiver, amount_tinybars) - - print(f"šŸ” Transaction ID: {txn_id}") - print("\nšŸ“Š Checking updated balances:") - account_balance_query(client, sender) - account_balance_query(client, receiver) - - -if __name__ == "__main__": - main() diff --git a/examples/transfer_token.py b/examples/transfer_token.py index 070fe2a2d..b9d4308d2 100644 --- a/examples/transfer_token.py +++ b/examples/transfer_token.py @@ -1,7 +1,6 @@ """ uv run examples/transfer_token.py python examples/transfer_token.py - """ import os import sys @@ -34,7 +33,6 @@ def setup_client(): operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY','')) client.set_operator(operator_id, operator_key) print(f"Client set up with operator id {client.operator_account_id}") - return client, operator_id, operator_key except (TypeError, ValueError): print("āŒ Error: Creating client, Please check your .env file") @@ -55,12 +53,13 @@ def create_account(client, operator_key): recipient_id = receipt.account_id print(f"āœ… Success! Created a new recipient account with ID: {recipient_id}") return recipient_id, recipient_key - except Exception as e: print(f"Error creating new account: {e}") sys.exit(1) + def create_token(client, operator_id, operator_key): + """Create a new token""" print("\nSTEP 2: Creating a new token...") try: token_tx = ( @@ -81,7 +80,9 @@ def create_token(client, operator_id, operator_key): print(f"āŒ Error creating token: {e}") sys.exit(1) + def associate_token(client, recipient_id, recipient_key, token_id): + """Associate token with the recipient""" print("\nSTEP 3: Associating Token...") try: association_tx = ( @@ -96,57 +97,53 @@ def associate_token(client, recipient_id, recipient_key, token_id): print(f"āŒ Error associating token: {e}") sys.exit(1) -def transfer_tokens(): - """ - A full example to create a new recipent account, a fungible token, and - transfer the token to that account - """ - # Config Client - client, operator_id, operator_key = setup_client() - - # Create a new recipient account. - recipient_id, recipient_key = create_account(client, operator_key) - - # Create new tokens. - token_id = create_token(client, operator_id, operator_key) - - # Associate Token - associate_token(client, recipient_id, recipient_key, token_id) - # Transfer Token - print("\nSTEP 4: Transfering Token...") +def transfer_transaction(client, operator_id, operator_key, recipient_id, token_id): + """Perform the token transfer""" + print("\nSTEP 4: Transferring Token...") try: - # Check balance before transfer + # Check balance before balance_before = ( CryptoGetAccountBalanceQuery(account_id=recipient_id) .execute(client) .token_balances ) - print("Token balance before token transfer:") + print("Token balance before transfer:") print(f"{token_id}: {balance_before.get(token_id)}") - transfer_tx = ( + tx = ( TransferTransaction() .add_token_transfer(token_id, operator_id, -1) .add_token_transfer(token_id, recipient_id, 1) .freeze_with(client) .sign(operator_key) ) - transfer_tx.execute(client) - + tx.execute(client) + print("\nāœ… Success! Token transfer complete.\n") - # Check balance after transfer + # Check balance after balance_after = ( CryptoGetAccountBalanceQuery(account_id=recipient_id) .execute(client) .token_balances ) - print("Token balance after token transfer:") + print("Token balance after transfer:") print(f"{token_id}: {balance_after.get(token_id)}") + except Exception as e: print(f"āŒ Error transferring token: {str(e)}") sys.exit(1) + +def main(): + """Main script runner""" + client, operator_id, operator_key = setup_client() + recipient_id, recipient_key = create_account(client, operator_key) + token_id = create_token(client, operator_id, operator_key) + associate_token(client, recipient_id, recipient_key, token_id) + transfer_transaction(client, operator_id, operator_key, recipient_id, token_id) + + if __name__ == "__main__": - transfer_tokens() + main() From f431406e82ab062cd962b5debe5e4454df83adde Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Sat, 22 Nov 2025 17:11:30 +0530 Subject: [PATCH 5/6] docs: add changelog entry for modular transfer_token refactor (#773) Signed-off-by: Raja Rathour --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f4a1b2a3..b29c4632e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,23 +25,26 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - `alias`, `staked_account_id`, `staked_node_id` and `decline_staking_reward` fields to AccountCreateTransaction - `staked_account_id`, `staked_node_id` and `decline_staking_reward` fields to AccountInfo - Added `examples/token_create_transaction_supply_key.py` to demonstrate token creation with and without a supply key. +- Added `examples/token_create_transaction_kyc_key.py` to demonstrate KYC key functionality, including creating tokens with/without KYC keys, granting/revoking KYC status, and understanding KYC requirements for token transfers. - Added BatchTransaction class - Add support for token metadata (bytes, max 100 bytes) in `TokenCreateTransaction`, including a new `set_metadata` setter, example, and tests. [#799] - - Added `examples/token_create_transaction_token_fee_schedule.py` to demonstrate creating tokens with custom fee schedules and the consequences of not having it. +- Added `examples/token_create_transaction_wipe_key.py` to demonstrate token wiping and the role of the wipe key. +- Refactored examples/transfer_token.py into modular functions (setup_client(), create_account(), create_token(), associate_token(), transfer_tokens()) and introduced a clear main() execution flow for improved readability, reuse, and maintainability while preserving identical runtime behavior (#773). ### Changed - Upgraded step-security/harden-runner v2.13.2 - bumped actions/checkout from 5.0.0 to 6.0.0 - Limit workflow bot to one message per PR - Refactored token-related example scripts (`token_delete.py`, `token_dissociate.py`, etc.) for improved readability and modularity. [#370] -- upgrade: step security action upgraded from harden-runner-2.13.1 to harden-runner. +- upgrade: step security action upgraded from harden-runner-2.13.1 to harden-runner-2.13.1 - chore: Split `examples/account_allowance_nft.py` into separate `account_allowance_approve_transaction_nft.py` and `account_allowance_delete_transaction_nft.py` examples. - chore: bump protobuf from 6.33.0 to 6.33.1 (#796) - fix: Allow `max_automatic_token_associations` to be set to -1 (unlimited) in `AccountCreateTransaction` and add field to `AccountInfo`. - Allow `PrivateKey` to be used for keys in `TopicCreateTransaction` for consistency. - Update github actions checkout from 5.0.0 to 5.0.1 (#814) - changed to add concurrency to workflow bot + ### Fixed - chore: fix test.yml workflow to log import errors (#740) From 45a0dc484588ccbe798f027a0da49c16f7416f69 Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Tue, 25 Nov 2025 10:37:25 +0530 Subject: [PATCH 6/6] Updating token_transfer.py Signed-off-by: Raja Rathour --- examples/transfer_token.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/transfer_token.py b/examples/transfer_token.py index b9d4308d2..9ca35d255 100644 --- a/examples/transfer_token.py +++ b/examples/transfer_token.py @@ -22,6 +22,7 @@ load_dotenv() network_name = os.getenv('NETWORK', 'testnet').lower() + def setup_client(): """Initialize and set up the client with operator account""" network = Network(network_name) @@ -29,8 +30,8 @@ def setup_client(): client = Client(network) try: - operator_id = AccountId.from_string(os.getenv('OPERATOR_ID','')) - operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY','')) + 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) print(f"Client set up with operator id {client.operator_account_id}") return client, operator_id, operator_key @@ -98,7 +99,7 @@ def associate_token(client, recipient_id, recipient_key, token_id): sys.exit(1) -def transfer_transaction(client, operator_id, operator_key, recipient_id, token_id): +def transfer_tokens(client, operator_id, operator_key, recipient_id, token_id): """Perform the token transfer""" print("\nSTEP 4: Transferring Token...") try: @@ -142,7 +143,7 @@ def main(): recipient_id, recipient_key = create_account(client, operator_key) token_id = create_token(client, operator_id, operator_key) associate_token(client, recipient_id, recipient_key, token_id) - transfer_transaction(client, operator_id, operator_key, recipient_id, token_id) + transfer_tokens(client, operator_id, operator_key, recipient_id, token_id) if __name__ == "__main__":