Skip to content

Commit 119a0cb

Browse files
committed
account balance query in readme and with flexible syntax
1 parent d585078 commit 119a0cb

File tree

6 files changed

+153
-39
lines changed

6 files changed

+153
-39
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This is a Python SDK for interacting with the Hedera Hashgraph platform. It allo
1414
- [Running Tests](#running-tests)
1515
- [Usage](#usage)
1616
- [Creating an Account](#creating-an-account)
17+
- [Querying Account Balance](#querying-account-balance)
1718
- [Creating a Token](#creating-a-token)
1819
- [Associating a Token](#associating-a-token)
1920
- [Transferring Tokens](#transferring-tokens)
@@ -155,6 +156,17 @@ transaction = (
155156
transaction.execute(client)
156157
```
157158

159+
### Querying Account Balance
160+
161+
#### Pythonic Syntax:
162+
```
163+
balance = CryptoGetAccountBalanceQuery(account_id=some_account_id).execute(client) print(f"Account balance: {balance.hbars} hbars")
164+
```
165+
166+
#### Method Chaining:
167+
```
168+
balance = ( CryptoGetAccountBalanceQuery() .set_account_id(some_account_id) .execute(client) ) print(f"Account balance: {balance.hbars} hbars")
169+
```
158170

159171
### Creating a Token
160172

examples/query_balance.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,21 @@
1818
def create_account_and_transfer():
1919
network = Network(network='testnet')
2020
client = Client(network)
21+
2122
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
2223
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
2324
client.set_operator(operator_id, operator_key)
2425

2526
new_account_private_key = PrivateKey.generate()
2627
new_account_public_key = new_account_private_key.public_key()
27-
28-
initial_balance = Hbar(10)
29-
transaction = (
30-
AccountCreateTransaction()
31-
.set_key(new_account_public_key)
32-
.set_initial_balance(initial_balance)
33-
.set_account_memo("New Account")
34-
.freeze_with(client)
35-
)
28+
transaction = AccountCreateTransaction(
29+
key=new_account_public_key,
30+
initial_balance=Hbar(10),
31+
memo="New Account"
32+
).freeze_with(client)
3633
transaction.sign(operator_key)
3734
receipt = transaction.execute(client)
3835
new_account_id = receipt.accountId
39-
print(f"New account created with ID: {new_account_id}")
4036

4137
balance_query = CryptoGetAccountBalanceQuery().set_account_id(new_account_id)
4238
balance = balance_query.execute(client)
@@ -48,8 +44,8 @@ def create_account_and_transfer():
4844
.add_hbar_transfer(operator_id, -transfer_amount.to_tinybars())
4945
.add_hbar_transfer(new_account_id, transfer_amount.to_tinybars())
5046
.freeze_with(client)
51-
.sign(operator_key)
5247
)
48+
transfer_transaction.sign(operator_key)
5349
transfer_receipt = transfer_transaction.execute(client)
5450
print(f"Transfer transaction status: {ResponseCode.get_name(transfer_receipt.status)}")
5551

@@ -60,4 +56,4 @@ def create_account_and_transfer():
6056
print(f"Updated balance of new account: {updated_balance.hbars} hbars")
6157

6258
if __name__ == "__main__":
63-
create_account_and_transfer()
59+
create_account_and_transfer()

src/hedera_sdk_python/account/account_create_transaction.py

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Union
2+
13
from hedera_sdk_python.transaction.transaction import Transaction
24
from hedera_sdk_python.hapi.services import crypto_create_pb2, duration_pb2
35
from hedera_sdk_python.response_code import ResponseCode
@@ -10,15 +12,22 @@ class AccountCreateTransaction(Transaction):
1012
Represents an account creation transaction on the Hedera network.
1113
"""
1214

13-
def __init__(self, key=None, initial_balance=0, receiver_signature_required=False, auto_renew_period=7890000, memo=""):
15+
def __init__(
16+
self,
17+
key: PublicKey = None,
18+
initial_balance: Union[Hbar, int] = 0,
19+
receiver_signature_required: bool = False,
20+
auto_renew_period: int = 7890000, # 90 days in seconds
21+
memo: str = ""
22+
):
1423
"""
15-
Initializes a new AccountCreateTransaction instance with default values or keyword arguments.
24+
Initializes a new AccountCreateTransaction instance with default values or specified keyword arguments.
1625
1726
Args:
1827
key (PublicKey, optional): The public key for the new account.
19-
initial_balance (int or Hbar, optional): Initial balance in tinybars or as an Hbar instance.
28+
initial_balance (Hbar or int, optional): Initial balance in Hbar or tinybars.
2029
receiver_signature_required (bool, optional): Whether receiver signature is required.
21-
auto_renew_period (int, optional): Auto-renew period in seconds (default is 90 days).
30+
auto_renew_period (int, optional): Auto-renew period in seconds (default is ~90 days).
2231
memo (str, optional): Memo for the account.
2332
"""
2433
super().__init__()
@@ -27,32 +36,78 @@ def __init__(self, key=None, initial_balance=0, receiver_signature_required=Fals
2736
self.receiver_signature_required = receiver_signature_required
2837
self.auto_renew_period = auto_renew_period
2938
self.account_memo = memo
30-
3139
self._default_transaction_fee = 300_000_000
3240

33-
def set_initial_balance(self, balance):
41+
def set_key(self, key: PublicKey):
42+
"""
43+
Sets the public key for the new account.
44+
45+
Args:
46+
key (PublicKey): The public key to assign to the account.
47+
48+
Returns:
49+
AccountCreateTransaction: The current transaction instance for method chaining.
50+
"""
3451
self._require_not_frozen()
35-
if not isinstance(balance, (Hbar, int)):
36-
raise TypeError("initial_balance must be an instance of Hbar or int representing tinybars.")
37-
self.initial_balance = balance
52+
self.key = key
3853
return self
3954

40-
def set_key(self, key: PublicKey):
55+
def set_initial_balance(self, balance: Union[Hbar, int]):
56+
"""
57+
Sets the initial balance for the new account.
58+
59+
Args:
60+
balance (Hbar or int): The initial balance in Hbar or tinybars.
61+
62+
Returns:
63+
AccountCreateTransaction: The current transaction instance for method chaining.
64+
"""
4165
self._require_not_frozen()
42-
self.key = key
66+
if not isinstance(balance, (Hbar, int)):
67+
raise TypeError(
68+
"initial_balance must be either an instance of Hbar or an integer (tinybars)."
69+
)
70+
self.initial_balance = balance
4371
return self
4472

4573
def set_receiver_signature_required(self, required: bool):
74+
"""
75+
Sets whether a receiver signature is required.
76+
77+
Args:
78+
required (bool): True if required, False otherwise.
79+
80+
Returns:
81+
AccountCreateTransaction: The current transaction instance for method chaining.
82+
"""
4683
self._require_not_frozen()
4784
self.receiver_signature_required = required
4885
return self
4986

5087
def set_auto_renew_period(self, seconds: int):
88+
"""
89+
Sets the auto-renew period in seconds.
90+
91+
Args:
92+
seconds (int): The auto-renew period.
93+
94+
Returns:
95+
AccountCreateTransaction: The current transaction instance for method chaining.
96+
"""
5197
self._require_not_frozen()
5298
self.auto_renew_period = seconds
5399
return self
54100

55101
def set_account_memo(self, memo: str):
102+
"""
103+
Sets the memo for the new account.
104+
105+
Args:
106+
memo (str): The memo to associate with the account.
107+
108+
Returns:
109+
AccountCreateTransaction: The current transaction instance for method chaining.
110+
"""
56111
self._require_not_frozen()
57112
self.account_memo = memo
58113
return self
@@ -66,18 +121,17 @@ def build_transaction_body(self):
66121
67122
Raises:
68123
ValueError: If required fields are missing.
124+
TypeError: If initial_balance is an invalid type.
69125
"""
70126
if not self.key:
71-
raise ValueError("Key must be set.")
72-
73-
if self.initial_balance is None:
74-
initial_balance_tinybars = 0
75-
elif isinstance(self.initial_balance, Hbar):
127+
raise ValueError("Key must be set before building the transaction.")
128+
129+
if isinstance(self.initial_balance, Hbar):
76130
initial_balance_tinybars = self.initial_balance.to_tinybars()
77131
elif isinstance(self.initial_balance, int):
78132
initial_balance_tinybars = self.initial_balance
79133
else:
80-
raise TypeError("initial_balance must be an instance of Hbar or int representing tinybars.")
134+
raise TypeError("initial_balance must be Hbar or int (tinybars).")
81135

82136
crypto_create_body = crypto_create_pb2.CryptoCreateTransactionBody(
83137
key=self.key.to_proto(),
@@ -111,7 +165,9 @@ def _execute_transaction(self, client, transaction_proto):
111165
if response.nodeTransactionPrecheckCode != ResponseCode.OK:
112166
error_code = response.nodeTransactionPrecheckCode
113167
error_message = ResponseCode.get_name(error_code)
114-
raise Exception(f"Error during transaction submission: {error_code} ({error_message})")
168+
raise Exception(
169+
f"Error during transaction submission: {error_code} ({error_message})"
170+
)
115171

116172
receipt = self.get_receipt(client)
117173
return receipt
@@ -121,7 +177,7 @@ def get_receipt(self, client, timeout=60):
121177
Retrieves the receipt for the transaction.
122178
123179
Args:
124-
client (Client): The client instance.
180+
client (Client): The client instance to query.
125181
timeout (int): Maximum time in seconds to wait for the receipt.
126182
127183
Returns:
@@ -131,7 +187,5 @@ def get_receipt(self, client, timeout=60):
131187
Exception: If the transaction ID is not set or if receipt retrieval fails.
132188
"""
133189
if self.transaction_id is None:
134-
raise Exception("Transaction ID is not set.")
135-
136-
receipt = client.get_transaction_receipt(self.transaction_id, timeout)
137-
return receipt
190+
raise Exception("Transaction ID is not set. Did you forget to sign or execute the transaction?")
191+
return client.get_transaction_receipt(self.transaction_id, timeout)

src/hedera_sdk_python/query/account_balance_query.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ class CryptoGetAccountBalanceQuery(Query):
1212
including hbars and tokens.
1313
"""
1414

15-
def __init__(self):
16-
"""Initializes a new instance of the CryptoGetAccountBalanceQuery class."""
15+
def __init__(self, account_id: AccountId = None):
16+
"""
17+
Initializes a new instance of the CryptoGetAccountBalanceQuery class.
18+
19+
Args:
20+
account_id (AccountId, optional): The ID of the account to retrieve the balance for.
21+
"""
1722
super().__init__()
18-
self.account_id = None
23+
self.account_id = account_id
1924

2025
def set_account_id(self, account_id: AccountId):
2126
"""

test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from hedera_sdk_python.consensus.topic_delete_transaction import TopicDeleteTransaction
1818
from hedera_sdk_python.consensus.topic_id import TopicId
1919
from hedera_sdk_python.query.topic_info_query import TopicInfoQuery
20+
from hedera_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
2021

2122
load_dotenv()
2223

@@ -57,6 +58,11 @@ def create_new_account(client, initial_balance=100000000):
5758

5859
return new_account_id, new_account_private_key
5960

61+
def query_balance(client, account_id):
62+
balance = CryptoGetAccountBalanceQuery(account_id=account_id).execute(client)
63+
print(f"Account {account_id} balance: {balance.hbars}")
64+
return balance
65+
6066
def create_token(client, operator_id, admin_key):
6167
transaction = TokenCreateTransaction(
6268
token_name="ExampleToken",
@@ -240,6 +246,7 @@ def main():
240246
client.set_operator(operator_id, operator_key)
241247

242248
recipient_id, recipient_private_key = create_new_account(client)
249+
query_balance(client, recipient_id)
243250

244251
token_id = create_token(client, operator_id, admin_key)
245252
associate_token(client, recipient_id, recipient_private_key, token_id)
@@ -249,7 +256,7 @@ def main():
249256
topic_id = create_topic(client)
250257
submit_message(client, topic_id)
251258
update_topic(client, topic_id)
252-
# query_topic_info(client, topic_id)
259+
query_topic_info(client, topic_id)
253260
delete_topic(client, topic_id)
254261

255262
if __name__ == "__main__":

tests/test_account_balance_query.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pytest
2+
from unittest.mock import MagicMock
3+
from hedera_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
4+
from hedera_sdk_python.account.account_id import AccountId
5+
from hedera_sdk_python.account.account_balance import AccountBalance
6+
from hedera_sdk_python.client.client import Client
7+
from hedera_sdk_python.hbar import Hbar
8+
from hedera_sdk_python.response_code import ResponseCode
9+
10+
@pytest.mark.usefixtures("mock_account_ids")
11+
def test_build_account_balance_query(mock_account_ids):
12+
"""
13+
Test building a CryptoGetAccountBalanceQuery with a valid account ID.
14+
"""
15+
account_id_sender, account_id_recipient, node_account_id, token_id_1, token_id_2 = mock_account_ids
16+
query = CryptoGetAccountBalanceQuery(account_id=account_id_sender)
17+
assert query.account_id == account_id_sender
18+
19+
@pytest.mark.usefixtures("mock_account_ids")
20+
def test_execute_account_balance_query(mock_account_ids):
21+
"""
22+
Test executing the CryptoGetAccountBalanceQuery with a mocked client.
23+
"""
24+
account_id_sender, account_id_recipient, node_account_id, token_id_1, token_id_2 = mock_account_ids
25+
query = CryptoGetAccountBalanceQuery().set_account_id(account_id_sender)
26+
mock_client = MagicMock(spec=Client)
27+
mock_response = MagicMock()
28+
mock_response.cryptogetAccountBalance.balance = 100000000
29+
mock_response.cryptogetAccountBalance.tokenBalances = []
30+
mock_response.cryptogetAccountBalance.header.nodeTransactionPrecheckCode = ResponseCode.OK
31+
mock_client.send_query = MagicMock(return_value=mock_response)
32+
query.node_account_ids = [node_account_id]
33+
query._make_request = MagicMock(return_value="fake_request")
34+
query._get_status_from_response = MagicMock(return_value=ResponseCode.OK)
35+
query._map_response = MagicMock(
36+
return_value=AccountBalance(Hbar.from_tinybars(100000000))
37+
)
38+
balance = query.execute(mock_client)
39+
assert balance.hbars.to_tinybars() == 100000000
40+
print("Test passed: CryptoGetAccountBalanceQuery executed successfully.")

0 commit comments

Comments
 (0)