-
Notifications
You must be signed in to change notification settings - Fork 108
feat: support hbar transfer #1014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
1fb81ce
70c5b1f
5fe2fde
0b1e7ac
22bd4fa
0f4d7ff
5affb9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,35 +7,19 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. | |
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
| - Modularized `transfer_transaction_fungible` example by introducing `account_balance_query()` & `transfer_transaction()`.Renamed `transfer_tokens()` → `main()` | ||
| - Phase 2 of the inactivity-unassign bot:Automatically detects stale open pull requests (no commit activity for 21+ days), comments with a helpful InactivityBot message, closes the stale PR, and unassigns the contributor from the linked issue. | ||
| - Added **str**() to CustomFixedFee and updated examples and tests accordingly. | ||
| - Added a github template for good first issues | ||
| - Added `.github/workflows/bot-assignment-check.yml` to limit non-maintainers to 2 concurrent issue assignments. | ||
| - Added all missing fields to **str**() method and updated `test_tokem_info.py` | ||
|
|
||
| - 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) | ||
| - Made custom fraction fee end to end | ||
| - feat: AccountCreateTransaction now supports both PrivateKey and PublicKey [#939](https://github.com/hiero-ledger/hiero-sdk-python/issues/939) | ||
| - Added Acceptance Criteria section to Good First Issue template for better contributor guidance (#997) | ||
| - Added __str__() to CustomRoyaltyFee and updated examples and tests accordingly (#986) | ||
| - Restore bug and feature request issue templates (#996)(https://github.com/hiero-ledger/hiero-sdk-python/issues/996) | ||
| - 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` | ||
| - 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 | ||
|
|
||
| - Allow `PublicKey` for `TokenUpdateKeys` in `TokenUpdateTransaction`, enabling non-custodial workflows where operators can build transactions using only public keys (#934). | ||
| - Bump protobuf toml to protobuf==6.33.2 | ||
| - Added more tests to the CustomFee class for different functionalities (#991) | ||
| - | ||
|
|
||
| ### Fixed | ||
|
|
||
| - Fixed inactivity bot workflow not checking out repository before running (#964) | ||
| - Fixed the topic_message_query integarion test | ||
| - good first issue template yaml rendering | ||
| - | ||
|
|
||
| ### Breaking Change | ||
|
|
||
|
|
@@ -53,7 +37,7 @@ 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]. | ||
| - 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 | ||
|
|
@@ -68,6 +52,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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be move at line 53 |
||
|
|
||
| ## [0.1.9] - 2025-11-26 | ||
|
|
||
| ### Added | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 integer.") | ||
| raise ValueError("Amount must be a integer.") | ||
|
Comment on lines
151
to
+152
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd keep the previous version, is a bit cleaner and also here you have two if with quite te same condition:
|
||
| 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): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
|
@@ -29,7 +30,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. | ||
|
|
@@ -59,24 +61,35 @@ 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: | ||
| 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 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 value.") | ||
|
|
||
| if not isinstance(is_approved, bool): | ||
| raise TypeError("is_approved must be a boolean.") | ||
|
|
||
|
|
@@ -85,16 +98,17 @@ def _add_hbar_transfer( | |
| transfer.amount += amount | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what could happen if the |
||
| return self | ||
|
|
||
| self.hbar_transfers.append(HbarTransfer(account_id, amount, is_approved)) | ||
| 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. | ||
|
|
@@ -103,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. | ||
|
|
@@ -190,7 +204,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 +225,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 | ||
Uh oh!
There was an error while loading. Please reload this page.