From 1fb81ce2ed32164c930ae6117ef10c5dab7a33e7 Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Thu, 4 Dec 2025 13:01:44 +0530 Subject: [PATCH 1/5] fix: hbar transfer Signed-off-by: prajeeta pal --- CHANGELOG.md | 4 ++ .../abstract_token_transfer_transaction.py | 2 +- .../transfer_transaction_e2e_test.py | 45 +++++++++++++++++++ tests/unit/test_transfer_transaction.py | 4 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a598c2f0..9deb309db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,10 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - fixed workflow: changelog check with improved sensitivity to deletions, additions, new releases +### Breaking Changes + +- Changed error message in `TransferTransaction._add_hbar_transfer()` and `AbstractTokenTransferTransaction._add_token_transfer()` when amount is zero from "Amount must be a non-zero integer" to "Amount must be a non-zero value." for clarity and consistency. + ## [0.1.9] - 2025-11-26 ### Added diff --git a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py index cd3910217..790d3a0bd 100644 --- a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py +++ b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py @@ -149,7 +149,7 @@ def _add_token_transfer( if not isinstance(account_id, AccountId): raise TypeError("account_id must be an AccountId instance.") if not isinstance(amount, int) or amount == 0: - raise ValueError("Amount must be a non-zero integer.") + raise ValueError("Amount must be a non-zero value.") if expected_decimals is not None and not isinstance(expected_decimals, int): raise TypeError("expected_decimals must be an integer.") if not isinstance(is_approved, bool): diff --git a/tests/integration/transfer_transaction_e2e_test.py b/tests/integration/transfer_transaction_e2e_test.py index 1e037031b..2aa284251 100644 --- a/tests/integration/transfer_transaction_e2e_test.py +++ b/tests/integration/transfer_transaction_e2e_test.py @@ -7,6 +7,7 @@ from hiero_sdk_python.crypto.private_key import PrivateKey from hiero_sdk_python.exceptions import PrecheckError from hiero_sdk_python.hbar import Hbar +from hiero_sdk_python.hbar_unit import HbarUnit from hiero_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.tokens.nft_id import NftId @@ -448,3 +449,47 @@ def test_integration_transfer_transaction_approved_token_transfer(): finally: env.close() + + +@pytest.mark.integration +def test_integration_transfer_transaction_with_hbar_units(): + env = IntegrationTestEnv() + + try: + new_account_private_key = PrivateKey.generate() + new_account_public_key = new_account_private_key.public_key() + + initial_balance = Hbar(1) + + account_transaction = AccountCreateTransaction( + key=new_account_public_key, initial_balance=initial_balance, memo="Recipient Account" + ) + + receipt = account_transaction.execute(env.client) + + assert ( + receipt.status == ResponseCode.SUCCESS + ), f"Account creation failed with status: {ResponseCode(receipt.status).name}" + + account_id = receipt.account_id + assert account_id is not None + + transfer_transaction = TransferTransaction() + transfer_transaction.add_hbar_transfer(env.operator_id, Hbar(-1.5, HbarUnit.HBAR)) + transfer_transaction.add_hbar_transfer(account_id, Hbar(1.5, HbarUnit.HBAR)) + + receipt = transfer_transaction.execute(env.client) + + assert ( + receipt.status == ResponseCode.SUCCESS + ), f"Transfer failed with status: {ResponseCode(receipt.status).name}" + + query_transaction = CryptoGetAccountBalanceQuery(account_id) + balance = query_transaction.execute(env.client) + + expected_balance_tinybars = Hbar(1).to_tinybars() + Hbar(1.5, HbarUnit.HBAR).to_tinybars() + assert ( + balance and balance.hbars.to_tinybars() == expected_balance_tinybars + ), f"Expected balance: {expected_balance_tinybars}, actual balance: {balance.hbars.to_tinybars()}" + finally: + env.close() diff --git a/tests/unit/test_transfer_transaction.py b/tests/unit/test_transfer_transaction.py index 222303a75..6b8a4d365 100644 --- a/tests/unit/test_transfer_transaction.py +++ b/tests/unit/test_transfer_transaction.py @@ -284,11 +284,11 @@ def test_zero_amount_validation(mock_account_ids): transfer_tx = TransferTransaction() # Test zero HBAR amount should raise ValueError - with pytest.raises(ValueError, match="Amount must be a non-zero integer"): + with pytest.raises(ValueError, match="Amount must be a non-zero value"): transfer_tx.add_hbar_transfer(account_id_1, 0) # Test zero token amount should raise ValueError - with pytest.raises(ValueError, match="Amount must be a non-zero integer"): + with pytest.raises(ValueError, match="Amount must be a non-zero value"): transfer_tx.add_token_transfer(token_id_1, account_id_1, 0) From 70c5b1f1fda6d79a92a4dd8ecbb625ad506e2433 Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Thu, 4 Dec 2025 23:33:30 +0530 Subject: [PATCH 2/5] fix: hbar support Signed-off-by: prajeeta pal --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9deb309db..5de86382f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added `.github/workflows/bot-office-hours.yml` to automate the Weekly Office Hour Reminder. - feat: Implement account creation with EVM-style alias transaction example. -- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new chnagelog entries are added under [Unreleased]. -- Support for message chunking in `TopicSubmitMessageTransaction`. +- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new changelog entries are added under [Unreleased] +- feat: Allow `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. ### Changed From 5fe2fde323988228fbdeceffe9a616279fbc3212 Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Fri, 5 Dec 2025 15:45:13 +0530 Subject: [PATCH 3/5] fix: hbar support errors Signed-off-by: prajeeta pal --- .../abstract_token_transfer_transaction.py | 4 ++- .../transaction/transfer_transaction.py | 36 +++++++++++++------ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py index 790d3a0bd..e73d0929b 100644 --- a/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py +++ b/src/hiero_sdk_python/tokens/abstract_token_transfer_transaction.py @@ -149,7 +149,9 @@ def _add_token_transfer( if not isinstance(account_id, AccountId): raise TypeError("account_id must be an AccountId instance.") if not isinstance(amount, int) or amount == 0: - raise ValueError("Amount must be a non-zero value.") + raise ValueError("Amount must be a integer.") + if amount == 0: + raise ValueError("amount must be a non-zero value.") if expected_decimals is not None and not isinstance(expected_decimals, int): raise TypeError("expected_decimals must be an integer.") if not isinstance(is_approved, bool): diff --git a/src/hiero_sdk_python/transaction/transfer_transaction.py b/src/hiero_sdk_python/transaction/transfer_transaction.py index f630ed869..37541e14f 100644 --- a/src/hiero_sdk_python/transaction/transfer_transaction.py +++ b/src/hiero_sdk_python/transaction/transfer_transaction.py @@ -29,7 +29,8 @@ def __init__( self, hbar_transfers: Optional[Dict[AccountId, int]] = None, token_transfers: Optional[Dict[TokenId, Dict[AccountId, int]]] = None, - nft_transfers: Optional[Dict[TokenId, List[Tuple[AccountId, AccountId, int, bool]]]] = None, + nft_transfers: Optional[Dict[TokenId, + List[Tuple[AccountId, AccountId, int, bool]]]] = None, ) -> None: """ Initializes a new TransferTransaction instance. @@ -73,10 +74,19 @@ def _add_hbar_transfer( TransferTransaction: The current instance of the transaction for chaining. """ self._require_not_frozen() + if not isinstance(account_id, AccountId): raise TypeError("account_id must be an AccountId instance.") - if not isinstance(amount, int) or amount == 0: - raise ValueError("Amount must be a non-zero integer.") + + if amount is None: + raise TypeError("amount cannot be None.") + + if not isinstance(amount, int): + raise TypeError("amount must be an integer.") + + if amount == 0: + raise ValueError("amount must be a non-zero integer.") + if not isinstance(is_approved, bool): raise TypeError("is_approved must be a boolean.") @@ -85,8 +95,9 @@ def _add_hbar_transfer( transfer.amount += amount return self - self.hbar_transfers.append(HbarTransfer(account_id, amount, is_approved)) - return self + self.hbar_transfers.append( + HbarTransfer(account_id, amount, is_approved)) + return self def add_hbar_transfer(self, account_id: AccountId, amount: int) -> "TransferTransaction": """ @@ -190,7 +201,8 @@ def _from_protobuf(cls, transaction_body, body_bytes: bytes, sig_map): if crypto_transfer.HasField("transfers"): for account_amount in crypto_transfer.transfers.accountAmounts: - account_id = AccountId._from_proto(account_amount.accountID) + account_id = AccountId._from_proto( + account_amount.accountID) amount = account_amount.amount is_approved = account_amount.is_approval transaction.hbar_transfers.append( @@ -210,17 +222,21 @@ def _from_protobuf(cls, transaction_body, body_bytes: bytes, sig_map): expected_decimals = token_transfer_list.expected_decimals.value transaction.token_transfers[token_id].append( - TokenTransfer(token_id, account_id, amount, expected_decimals, is_approved) + TokenTransfer(token_id, account_id, amount, + expected_decimals, is_approved) ) for nft_transfer in token_transfer_list.nftTransfers: - sender_id = AccountId._from_proto(nft_transfer.senderAccountID) - receiver_id = AccountId._from_proto(nft_transfer.receiverAccountID) + sender_id = AccountId._from_proto( + nft_transfer.senderAccountID) + receiver_id = AccountId._from_proto( + nft_transfer.receiverAccountID) serial_number = nft_transfer.serialNumber is_approved = nft_transfer.is_approval transaction.nft_transfers[token_id].append( - TokenNftTransfer(token_id, sender_id, receiver_id, serial_number, is_approved) + TokenNftTransfer( + token_id, sender_id, receiver_id, serial_number, is_approved) ) return transaction From 0b1e7ac766a9b67cd299ffa7e2431dc66668fa1c Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Tue, 9 Dec 2025 10:04:17 +0530 Subject: [PATCH 4/5] feat: hbar support Signed-off-by: prajeeta pal --- CHANGELOG.md | 12 ++++++-- .../transaction/transfer_transaction.py | 29 ++++++++++--------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5de86382f..885da86c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,21 +7,28 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ## [Unreleased] ### Added + - Add examples/tokens/token_create_transaction_pause_key.py example demonstrating token pause/unpause behavior and pause key usage (#833) - Added `docs/sdk_developers/training/transaction_lifecycle.md` to explain the typical lifecycle of executing a transaction using the Hedera Python SDK. - Add inactivity bot workflow to unassign stale issue assignees (#952) +- feat: Allow `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. + ### Changed + - ### Fixed + - ### Breaking Change + - ## [0.1.10] - 2025-12-03 ### Added + - Added docs/sdk_developers/training/workflow: a training for developers to learn the workflow to contribute to the python SDK. - Added Improved NFT allowance deletion flow with receipt-based status checks and strict `SPENDER_DOES_NOT_HAVE_ALLOWANCE` verification. - Add `max_automatic_token_associations`, `staked_account_id`, `staked_node_id` and `decline_staking_reward` fields to `AccountUpdateTransaction` (#801) @@ -30,8 +37,8 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Added `.github/workflows/merge-conflict-bot.yml` to automatically detect and notify users of merge conflicts in Pull Requests. - Added `.github/workflows/bot-office-hours.yml` to automate the Weekly Office Hour Reminder. - feat: Implement account creation with EVM-style alias transaction example. -- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new changelog entries are added under [Unreleased] -- feat: Allow `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. +- Added validation logic in `.github/workflows/pr-checks.yml` to detect when no new changelog entries are added under [Unreleased]. +- Support for message chunking in `TopicSubmitMessageTransaction`. ### Changed @@ -41,7 +48,6 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - feat: Add string representation method for `CustomFractionalFee` class and update `custom_fractional_fee.py` example. - Moved query examples to their respective domain folders to improve structure matching. - ### Fixed - fixed workflow: changelog check with improved sensitivity to deletions, additions, new releases diff --git a/src/hiero_sdk_python/transaction/transfer_transaction.py b/src/hiero_sdk_python/transaction/transfer_transaction.py index 37541e14f..fe96b49d3 100644 --- a/src/hiero_sdk_python/transaction/transfer_transaction.py +++ b/src/hiero_sdk_python/transaction/transfer_transaction.py @@ -2,11 +2,12 @@ Defines TransferTransaction for transferring HBAR or tokens between accounts. """ -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Tuple, Union from hiero_sdk_python.account.account_id import AccountId from hiero_sdk_python.channels import _Channel from hiero_sdk_python.executable import _Method +from hiero_sdk_python.hbar import Hbar from hiero_sdk_python.hapi.services import basic_types_pb2, crypto_transfer_pb2, transaction_pb2 from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import ( SchedulableTransactionBody, @@ -60,14 +61,14 @@ def _init_hbar_transfers(self, hbar_transfers: Dict[AccountId, int]) -> None: self.add_hbar_transfer(account_id, amount) def _add_hbar_transfer( - self, account_id: AccountId, amount: int, is_approved: bool = False + self, account_id: AccountId, amount: Union[int, Hbar], is_approved: bool = False ) -> "TransferTransaction": """ Internal method to add a HBAR transfer to the transaction. Args: account_id (AccountId): The account ID of the sender or receiver. - amount (int): The amount of the HBAR to transfer. + amount (Union[int, Hbar]): The amount of the HBAR to transfer (in tinybars if int, or Hbar object). is_approved (bool, optional): Whether the transfer is approved. Defaults to False. Returns: @@ -81,11 +82,13 @@ def _add_hbar_transfer( if amount is None: raise TypeError("amount cannot be None.") - if not isinstance(amount, int): - raise TypeError("amount must be an integer.") + if isinstance(amount, Hbar): + amount = amount.to_tinybars() + elif not isinstance(amount, int): + raise TypeError("amount must be an integer or Hbar object.") if amount == 0: - raise ValueError("amount must be a non-zero integer.") + raise ValueError("Amount must be a non-zero value.") if not isinstance(is_approved, bool): raise TypeError("is_approved must be a boolean.") @@ -95,17 +98,17 @@ def _add_hbar_transfer( transfer.amount += amount return self - self.hbar_transfers.append( - HbarTransfer(account_id, amount, is_approved)) - return self + self.hbar_transfers.append( + HbarTransfer(account_id, amount, is_approved)) + return self - def add_hbar_transfer(self, account_id: AccountId, amount: int) -> "TransferTransaction": + def add_hbar_transfer(self, account_id: AccountId, amount: Union[int, Hbar]) -> "TransferTransaction": """ Adds a HBAR transfer to the transaction. Args: account_id (AccountId): The account ID of the sender or receiver. - amount (int): The amount of the HBAR to transfer. + amount (Union[int, Hbar]): The amount of the HBAR to transfer (in tinybars if int, or Hbar object). Returns: TransferTransaction: The current instance of the transaction for chaining. @@ -114,14 +117,14 @@ def add_hbar_transfer(self, account_id: AccountId, amount: int) -> "TransferTran return self def add_approved_hbar_transfer( - self, account_id: AccountId, amount: int + self, account_id: AccountId, amount: Union[int, Hbar] ) -> "TransferTransaction": """ Adds a HBAR transfer with approval to the transaction. Args: account_id (AccountId): The account ID of the sender or receiver. - amount (int): The amount of the HBAR to transfer. + amount (Union[int, Hbar]): The amount of the HBAR to transfer (in tinybars if int, or Hbar object). Returns: TransferTransaction: The current instance of the transaction for chaining. From 5affb9b62865e218e47a1f353ec9bea9ada35909 Mon Sep 17 00:00:00 2001 From: prajeeta pal Date: Fri, 12 Dec 2025 23:59:18 +0530 Subject: [PATCH 5/5] feat: support hbar transfer Signed-off-by: prajeeta pal --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0539cf23c..31936cbe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,9 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Support selecting specific node account ID(s) for queries and transactions and added `Network._get_node()` with updated execution flow (#362) - Add TLS support with two-stage control (`set_transport_security()` and `set_verify_certificates()`) for encrypted connections to Hedera networks. TLS is enabled by default for hosted networks (mainnet, testnet, previewnet) and disabled for local networks (solo, localhost) (#855) - Add PR inactivity reminder bot for stale pull requests `.github/workflows/pr-inactivity-reminder-bot.yml` -- Add comprehensive training documentation for _Executable class `docs/sdk_developers/training/executable.md` +- Add comprehensive training documentation for \_Executable class `docs/sdk_developers/training/executable.md` - Added empty `docs/maintainers/good_first_issues.md` file for maintainers to write Good First Issue guidelines (#1034) -- feat: Allow `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. +- Added `add_hbar_transfer`, `add_approved_hbar_transfer`, and internal `_add_hbar_transfer` to accept `Hbar` objects in addition to raw tinybar integers, with internal normalization to tinybars. Added tests validating the new behavior. ### Changed