Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
780941e
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
1b25945
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
0080627
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
b34cbb4
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
df9ac72
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
3a7b1a4
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
341585b
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
522c8d9
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 7, 2026
cd2a455
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
78afcc5
Merge branch 'main' into 1293-add-contract_id-support-for-CryptoGetAc…
AntonioCeppellini Jan 8, 2026
b776d26
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
238d85f
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
d70c6ef
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
1d0a3df
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
7e62179
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 8, 2026
fa540c7
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 11, 2026
ae391d0
Merge branch 'main' into 1293-add-contract_id-support-for-CryptoGetAc…
AntonioCeppellini Jan 11, 2026
33f2bd0
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 11, 2026
1130e58
feat: add contract_id support for CryptoGetAccountBalanceQuery
AntonioCeppellini Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
- Add CodeRabbit documentation review prompts for docs, sdk_users, and sdk_developers with priorities, philosophy, and edge case checks. ([#1236](https://github.com/hiero-ledger/hiero-sdk-python/issues/1236))
- Enhance NodeAddress tests with additional coverage for proto conversion `tests/unit/node_address_test.py`
- Updated `pyproject.toml` to enforce stricter Ruff linting rules, including Google-style docstrings (`D`), import sorting (`I`), and modern Python syntax (`UP`).
- Add contract_id support for CryptoGetAccountBalanceQuery([#1293](https://github.com/hiero-ledger/hiero-sdk-python/issues/1293))

### Fixed
- GFI workflow casing
Expand Down
95 changes: 95 additions & 0 deletions examples/query/contract_balance_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
Contract Balance Query Example

This script demonstrates how to query the balance of a *contract* using:
- CryptoGetAccountBalanceQuery().set_contract_id(...)

Run with:
uv run examples/query/contract_balance_query.py
python examples/query/contract_balance_query.py

Environment variables required:
- OPERATOR_ID
- OPERATOR_KEY
Optional:
- NETWORK (default: testnet)
- CONTRACT_ID (e.g. "0.0.1234")
"""

import os
import sys
from dotenv import load_dotenv

from hiero_sdk_python import Network, Client, AccountId, PrivateKey
from hiero_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
from hiero_sdk_python.contract.contract_id import ContractId

load_dotenv()
network_name = os.getenv("NETWORK", "testnet").lower()


def setup_client():
"""
Initialize and configure the Hiero SDK client with operator credentials.
"""
network = Network(network_name)
print(f"Connecting to Hedera {network_name} network!")
client = Client(network)

operator_id_str = os.getenv("OPERATOR_ID")
operator_key_str = os.getenv("OPERATOR_KEY")

if not operator_id_str or not operator_key_str:
raise ValueError("OPERATOR_ID and OPERATOR_KEY environment variables must be set")

operator_id = AccountId.from_string(operator_id_str)
operator_key = PrivateKey.from_string(operator_key_str)
client.set_operator(operator_id, operator_key)

print(f"Client set up with operator id {client.operator_account_id}")
return client


def get_contract_balance(client: Client, contract_id: ContractId):
"""
Query and retrieve the HBAR balance of a contract.

Use account_id when you want an *account* balance.
Use contract_id when you want a *contract* balance (smart contract entity).
"""
print(f"Querying balance for contract {contract_id} ...")

balance = CryptoGetAccountBalanceQuery().set_contract_id(contract_id).execute(client)

# AccountBalance object: print a friendly summary
print("✓ Balance retrieved successfully!")
print(f" Contract: {contract_id}")
print(f" Hbars: {balance.hbars}")
if getattr(balance, "token_balances", None):
print(f" Token balances entries: {len(balance.token_balances)}")
return balance


def main():
try:
client = setup_client()

contract_id_str = os.getenv("CONTRACT_ID")
if not contract_id_str:
raise ValueError("CONTRACT_ID environment variable must be set (e.g. '0.0.1234')")

contract_id = ContractId.from_string(contract_id_str)

# Uncommenting the following would raise due to oneof constraints:
# query = CryptoGetAccountBalanceQuery(account_id=client.operator_account_id, contract_id=contract_id)
# query.execute(client)

get_contract_balance(client, contract_id)

except Exception as e:
print(f"✗ Error: {e}", file=sys.stderr)
sys.exit(1)


if __name__ == "__main__":
main()
77 changes: 57 additions & 20 deletions src/hiero_sdk_python/query/account_balance_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from hiero_sdk_python.account.account_balance import AccountBalance
from hiero_sdk_python.executable import _Method
from hiero_sdk_python.channels import _Channel
from hiero_sdk_python.contract.contract_id import ContractId


class CryptoGetAccountBalanceQuery(Query):
"""
Expand All @@ -15,7 +17,11 @@ class CryptoGetAccountBalanceQuery(Query):
including hbars and tokens.
"""

def __init__(self, account_id: Optional[AccountId] = None) -> None:
def __init__(
self,
account_id: Optional[AccountId] = None,
contract_id: Optional[ContractId] = None,
) -> None:
"""
Initializes a new instance of the CryptoGetAccountBalanceQuery class.

Expand All @@ -24,20 +30,38 @@ def __init__(self, account_id: Optional[AccountId] = None) -> None:
"""
super().__init__()
self.account_id: Optional[AccountId] = account_id
self.contract_id: Optional[ContractId] = contract_id

def set_account_id(self, account_id: AccountId) -> "CryptoGetAccountBalanceQuery":
"""
Sets the account ID for which to retrieve the balance.
Resets to None the contract ID.

Args:
account_id (AccountId): The ID of the account.

Returns:
CryptoGetAccountBalanceQuery: The current instance for method chaining.
"""
self.contract_id = None
self.account_id = account_id
return self

def set_contract_id(self, contract_id: ContractId) -> "CryptoGetAccountBalanceQuery":
"""
Sets the contract ID for which to retrieve the balance.
Resets to None the account ID.

Args:
contract_id (ContractId): The ID of the contract.

Returns:
CryptoGetAccountBalanceQuery: The current instance for method chaining.
"""
self.account_id = None
self.contract_id = contract_id
return self

def _make_request(self) -> query_pb2.Query:
"""
Constructs the protobuf request for the account balance query.
Expand All @@ -46,22 +70,34 @@ def _make_request(self) -> query_pb2.Query:
query_pb2.Query: The protobuf Query object containing the account balance query.

Raises:
ValueError: If the account ID is not set.
ValueError: If both the account ID and contract ID are not set.
ValueError: If both the account ID and contract ID are set.
AttributeError: If the Query protobuf structure is invalid.
Exception: If any other error occurs during request construction.
"""
try:
if not self.account_id:
raise ValueError("Account ID must be set before making the request.")
if not self.account_id and not self.contract_id:
raise ValueError("Either Account ID or Contract ID must be set before making the request.")

if self.account_id and self.contract_id:
raise ValueError("Specify either Account ID or Contract ID, not both.")

query_header = self._make_request_header()
crypto_get_balance = crypto_get_account_balance_pb2.CryptoGetAccountBalanceQuery()
crypto_get_balance = (
crypto_get_account_balance_pb2.CryptoGetAccountBalanceQuery()
)
crypto_get_balance.header.CopyFrom(query_header)
crypto_get_balance.accountID.CopyFrom(self.account_id._to_proto())

if self.account_id:
crypto_get_balance.accountID.CopyFrom(self.account_id._to_proto())
else:
crypto_get_balance.contractID.CopyFrom(self.contract_id._to_proto())

query = query_pb2.Query()
if not hasattr(query, 'cryptogetAccountBalance'):
raise AttributeError("Query object has no attribute 'cryptogetAccountBalance'")
if not hasattr(query, "cryptogetAccountBalance"):
raise AttributeError(
"Query object has no attribute 'cryptogetAccountBalance'"
)
query.cryptogetAccountBalance.CopyFrom(crypto_get_balance)

return query
Expand All @@ -73,7 +109,7 @@ def _make_request(self) -> query_pb2.Query:
def _get_method(self, channel: _Channel) -> _Method:
"""
Returns the appropriate gRPC method for the account balance query.

Implements the abstract method from Query to provide the specific
gRPC method for getting account balances.

Expand All @@ -84,16 +120,15 @@ def _get_method(self, channel: _Channel) -> _Method:
_Method: The method wrapper containing the query function
"""
return _Method(
transaction_func=None,
query_func=channel.crypto.cryptoGetBalance
transaction_func=None, query_func=channel.crypto.cryptoGetBalance
)

def execute(self, client) -> AccountBalance:
"""
Executes the account balance query.

This function delegates the core logic to `_execute()`, and may propagate exceptions raised by it.

Sends the query to the Hedera network and processes the response
to return an AccountBalance object.

Expand All @@ -113,26 +148,28 @@ def execute(self, client) -> AccountBalance:

return AccountBalance._from_proto(response.cryptogetAccountBalance)

def _get_query_response(self, response: Any) -> crypto_get_account_balance_pb2.CryptoGetAccountBalanceResponse:
def _get_query_response(
self, response: Any
) -> crypto_get_account_balance_pb2.CryptoGetAccountBalanceResponse:
"""
Extracts the account balance response from the full response.

Implements the abstract method from Query to extract the
specific account balance response object.

Args:
response: The full response from the network

Returns:
The crypto get account balance response object
"""
return response.cryptogetAccountBalance

def _is_payment_required(self):
"""
Account balance query does not require payment.

Returns:
bool: False
"""
return False
return False
19 changes: 17 additions & 2 deletions tests/integration/account_balance_query_e2e_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import pytest

from hiero_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
from hiero_sdk_python.contract.contract_id import ContractId
from tests.integration.utils import IntegrationTestEnv


@pytest.mark.integration
def test_integration_account_balance_query_can_execute():
env = IntegrationTestEnv()

try:
CryptoGetAccountBalanceQuery(account_id=env.operator_id).execute(env.client)
finally:
env.close()
env.close()

@pytest.mark.integration
def test_integration_contract_balance_query_can_execute():
env = IntegrationTestEnv()

contract_id_str = os.getenv("HEDERA_TEST_CONTRACT_ID")
if not contract_id_str:
pytest.skip("Set HEDERA_TEST_CONTRACT_ID (e.g. '0.0.1234') to run this test.")

contract_id = ContractId.from_string(contract_id_str)

try:
CryptoGetAccountBalanceQuery().set_contract_id(contract_id).execute(env.client)
finally:
env.close()
Loading
Loading