Skip to content

Conversation

@manishdait
Copy link
Contributor

Description:

This PR introduces support for maximum query payment limits for Query class by:

  • Adding a max_query_payment field to the Query base class.
  • Providing a setter: set_max_query_payment(...)
  • Validating the actual cost against:
    • the query-specific max_query_payment, if set, or
    • the client’s default_max_query_payment as a fallback
  • Raising a clear and descriptive error if the query cost exceeds the configured maximum

Changes Made:

  • Added max_query_payment support to the Query base class.
  • Added set_max_query_payment() to allow setting a per-query maximum payment.
  • Added DEFAULT_MAX_QUERY_PAYMENT = Hbar(1) to client.
  • Added set_default_max_query_payment() on Client to define a client-wide default maximum query payment.

Related issue(s):

Fixes #1346

Notes for reviewer:

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

@codecov
Copy link

codecov bot commented Jan 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1347      +/-   ##
==========================================
+ Coverage   92.44%   92.47%   +0.02%     
==========================================
  Files         139      139              
  Lines        8528     8555      +27     
==========================================
+ Hits         7884     7911      +27     
  Misses        644      644              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Hi, this is WorkflowBot.
Your pull request cannot be merged as it is not passing all our workflow checks.
Please click on each check to review the logs and resolve issues so all checks pass.
To help you:

Copy link
Contributor

@aceppaluni aceppaluni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@manishdait , this is looking great!

The new default max_query_payment (1 HBAR) is correctly enforced, but it’s causing the Run Examples workflow to fail.

In examples/contract/ethereum_transaction.py, a ContractCallQuery is executed without setting max_query_payment. The reported query cost (~1.42 HBAR) exceeds the new default, so _before_execute() raises as expected.

Other examples and integration tests in this PR already follow the new pattern:

cost = query.get_cost(client)
query.set_max_query_payment(cost)
result = query.execute(client)

What do you think of updating the Ethereum transaction example to explicitly set max_query_payment in the same way?

@manishdait manishdait marked this pull request as ready for review January 6, 2026 04:30
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Adds per-query and client-wide maximum query payment controls. New Query.set_max_query_payment and Client.set_default_max_query_payment; queries fetch cost before execution and validate it against the per-query max or client default, raising an error if exceeded.

Changes

Cohort / File(s) Summary of changes
Core: Client
src/hiero_sdk_python/client/client.py
Added DEFAULT_MAX_QUERY_PAYMENT constant, default_max_query_payment attribute, and set_default_max_query_payment(...) with type/value validation and chaining.
Core: Query base
src/hiero_sdk_python/query/query.py
Added max_query_payment attribute and set_max_query_payment(...); _before_execute now resolves cost, selects max (query override or client default), and raises ValueError if cost exceeds max.
Examples (contract)
examples/contract/contract_call_query.py, examples/contract/contract_execute_transaction.py, examples/contract/ethereum_transaction.py
Converted inline execute flow to two-step: build query, call get_cost(client), set max via set_max_query_payment(cost), then execute(client).
Tests: unit
tests/unit/client_test.py, tests/unit/query_test.py
Added unit tests for set_default_max_query_payment and set_max_query_payment (type/value checks, conversions, override/interaction behavior); updated existing tests to use cost-before-execute pattern.
Tests: integration
tests/integration/query_e2e_test.py, tests/integration/contract_call_query_e2e_test.py
Added integration test asserting query fails when cost exceeds max; updated contract e2e tests to follow cost-aware flow.
Meta / Changelog
CHANGELOG.md
Documented addition of per-query and client-wide max payment configuration.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Client
participant Query
participant Node as Network/Node
Note over Client,Query #F8F0E3: Cost-aware query flow (new)
Client->>Query: build query (params)
Query->>Node: get_cost(request)
Node-->>Query: cost (Hbar)
Query->>Client: read default_max_query_payment
alt cost > max_allowed (query.max or client.default)
Query-->>Client: raise ValueError("query cost exceeds max")
else cost <= max_allowed
Query->>Query: prepare payment (payment_amount)
Query->>Node: execute(query + payment)
Node-->>Query: response
Query-->>Client: return result
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding max query payment support to the Query class, which is the primary focus of the PR.
Description check ✅ Passed The description is directly related to the changeset, explaining the max query payment support implementation, related changes, and linking to the relevant issue.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #1346: max_query_payment field, set_max_query_payment() setter, validation against per-query or client-wide defaults, and clear error messaging.
Out of Scope Changes check ✅ Passed All changes are within scope: Query and Client API additions for max payment support, examples showing usage patterns, and comprehensive unit/integration tests validating the feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/contract/ethereum_transaction.py (1)

149-164: Consider simplifying the cost-fetch-and-set pattern for better UX.

This is the third example file with the same cost-fetch-and-set pattern. While consistent across examples, the pattern remains redundant—fetching the cost just to set max_query_payment to that exact value doesn't effectively demonstrate protection against high query fees.

For better user experience and clearer demonstration of the feature, consider:

Option 1: Set client default once in setup_client():

client.set_default_max_query_payment(Hbar(10))

Option 2: Set a fixed max per query without fetching:

query = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .set_max_query_payment(Hbar(5))
)
result = query.execute(client)

If the cost-fetch pattern is intentional to guarantee execution success, consider adding a comment explaining why this approach is used rather than setting a client default.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0e44d3 and 44b253e.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • examples/contract/contract_call_query.py
  • examples/contract/contract_execute_transaction.py
  • examples/contract/ethereum_transaction.py
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/query/query.py
  • tests/integration/contract_call_query_e2e_test.py
  • tests/unit/client_test.py
  • tests/unit/query_test.py
🧰 Additional context used
📓 Path-based instructions (3)
examples/**/*

⚙️ CodeRabbit configuration file

examples/**/*: You are acting as a senior maintainer reviewing SDK examples. Your goal is to ensure examples work verbatim for users who copy-paste them.

Priority 1 - Correctness:

  • Verify transaction lifecycle chain (construction -> freeze_with -> sign -> execute).
  • Ensure freeze_with(client) is called BEFORE signing.
  • Validate that methods referenced actually exist in the hiero_sdk_python codebase.
  • Ensure response validation checks receipt.status against ResponseCode enums (e.g., ResponseCode.SUCCESS).

Priority 2 - Transaction Lifecycle:

  • Check method chaining logic.
  • Verify correct signing order (especially for multi-sig).
  • Ensure explicit .execute(client) calls.
  • Verify response property extraction (e.g., using .token_id, .account_id, .serial_numbers).
  • Ensure error handling uses ResponseCode(receipt.status).name for clarity.

Priority 3 - Naming & Clarity:

  • Enforce role-based naming: operator_id/_key, treasury_account_id/_key, receiver_id/_key.
  • Use _id suffix for AccountId and _key suffix for PrivateKey variables.
  • Validate negative examples explicitly check for failure codes (e.g., TOKEN_HAS_NO_PAUSE_KEY).
  • Ensure logical top-to-bottom flow without ambiguity.

Priority 4 - Consistency:

  • Verify standard patterns: def main(), if __name__ == "__main__":, load_dotenv().
  • IMPORT RULES:
    1. Accept both top-level imports (e.g., from hiero_sdk_python import PrivateKey) and fully qualified imports (e.g., from hiero_sdk_python.crypto.private_key import PrivateKey).
    2. STRICTLY validate that the import path actually exists in the project structure. Compare against other files in /examples or your knowledge of the SDK file tree.
    3. Flag hallucinations immediately (e.g., hiero_sdk_python.keys does not exist).
  • Check for try-except blocks with sys.exit(1) for critical failures.

Priority 5 - User Experience:

  • Ensure comments explain SDK usage patterns (for users,...

Files:

  • examples/contract/ethereum_transaction.py
  • examples/contract/contract_execute_transaction.py
  • examples/contract/contract_call_query.py
tests/unit/**/*

⚙️ CodeRabbit configuration file

tests/unit/**/*: You are acting as a senior maintainer reviewing unit tests for the hiero-sdk-python project. Your goal is to ensure tests are extensive, deterministic, and protect against breaking changes.

CRITICAL PRINCIPLES - Tests Must Fail Loudly & Deterministically:

  • Tests must provide useful error messages when they fail for future debugging.
  • No print() statements - use assertions with descriptive messages.
  • No timing-dependent or unseeded random assertions.
  • No network calls or external dependencies (unit tests are isolated).
  • No unjustified TODOs or skipped tests without tracking issues.

PRIORITY 1 - Protect Against Breaking Changes:

  • Assert public attributes exist (e.g., assert hasattr(obj, 'account_id')).
  • Assert return types where relevant (e.g., assert isinstance(result, AccountId)).
  • Assert fluent setters return self (e.g., assert tx.set_memo("test") is tx).
  • Assert backward-compatible defaults are maintained.
  • If a breaking change is introduced, tests must assert deprecation behavior and test old behavior until removal.

PRIORITY 2 - Constructor & Setter Behavior:

  • Test constructor behavior with valid inputs, edge cases, and invalid inputs.
  • Test setter behavior including method chaining (fluent interface).
  • Verify that setters validate input and raise appropriate exceptions.
  • Test that getters return expected values after construction/setting.

PRIORITY 3 - Comprehensive Coverage:

  • Unit tests should be extensive - test even if we don't expect users to use it currently.
  • Cover happy paths AND unhappy paths/edge cases.
  • Test boundary conditions, null/None values, empty collections, etc.
  • Avoid brittle ordering assertions unless order is part of the contract.

PRIORITY 4 - No Mocks for Non-Existent Modules:

  • All imports must reference actual SDK modules - no hallucinated paths.
  • Validate import paths against the actual src/hiero_sdk_python structure.
  • Mocks should only be used for external de...

Files:

  • tests/unit/client_test.py
  • tests/unit/query_test.py
tests/integration/**/*

⚙️ CodeRabbit configuration file

tests/integration/**/*: You are acting as a senior maintainer reviewing integration tests for the hiero-sdk-python project. Your goal is to ensure end-to-end tests validate real network behavior safely and deterministically.

CRITICAL PRINCIPLES - Safety & Diagnosability:

  • Prioritize safety: No implicit or default mainnet usage.
  • Secrets and credentials must be injected safely (env vars, not hardcoded).
  • Test failures must be diagnosable with clear error messages.
  • Tests must assert observable network behavior, not just SUCCESS.
  • Failure-path tests must assert specific ResponseCode values (e.g., TOKEN_HAS_NO_PAUSE_KEY).

PRIORITY 1 - End-to-End Behavior:

  • Tests should be end-to-end: construct → freeze → sign → execute → verify.
  • Validate resulting balances, ownership, and state changes (not just transaction success).
  • Assert transaction receipts contain expected data (IDs, serial numbers, etc.).
  • Verify network state after operations (e.g., account balance changed, token transferred).

PRIORITY 2 - Test Structure & Maintainability:

  • One major behavior per test (clear focus).
  • Tests should be readable: clear names, brief docstrings, key inline comments.
  • Minimal abstraction layers - prefer clarity over DRY.
  • Is the file too monolithic? Flag if tests should be split into smaller modules.
  • Are helper functions good candidates for pytest fixtures or shared utilities?

PRIORITY 3 - Isolation & Cleanup:

  • Local account creation over operator reuse (avoid state pollution).
  • Are accounts, tokens, and allowances properly cleaned up to avoid state leakage?
  • Recommend teardown strategies or fixture scoping improvements.
  • Tests should not depend on execution order (avoid brittle assumptions).

PRIORITY 4 - Assertions & Coverage:

  • Do tests validate only success/failure, or also assert resulting state?
  • Suggest additional assertions that would strengthen correctness (balances, allowances, ownership).
  • Cover happy paths AND ...

Files:

  • tests/integration/contract_call_query_e2e_test.py
🧬 Code graph analysis (8)
examples/contract/ethereum_transaction.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
tests/unit/client_test.py (2)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (4)
  • Client (32-287)
  • from_env (59-103)
  • for_testnet (106-115)
  • set_default_max_query_payment (249-274)
src/hiero_sdk_python/client/client.py (1)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
examples/contract/contract_execute_transaction.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
src/hiero_sdk_python/query/query.py (1)
src/hiero_sdk_python/hbar.py (2)
  • Hbar (18-213)
  • to_hbars (67-71)
tests/unit/query_test.py (3)
src/hiero_sdk_python/query/query.py (4)
  • set_max_query_payment (96-124)
  • _is_payment_required (414-421)
  • get_cost (281-321)
  • _before_execute (126-157)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (1)
  • set_default_max_query_payment (249-274)
tests/integration/contract_call_query_e2e_test.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • execute (190-216)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
examples/contract/contract_call_query.py (1)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
🪛 Ruff (0.14.10)
tests/unit/client_test.py

192-192: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

src/hiero_sdk_python/client/client.py

7-7: typing.List is deprecated, use list instead

(UP035)


249-249: Use X | Y for type annotations

Convert to X | Y

(UP007)


263-265: Avoid specifying long messages outside the exception class

(TRY003)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

src/hiero_sdk_python/query/query.py

96-96: Use X | Y for type annotations

Convert to X | Y

(UP007)


113-115: Avoid specifying long messages outside the exception class

(TRY003)


119-119: Avoid specifying long messages outside the exception class

(TRY003)


154-157: Avoid specifying long messages outside the exception class

(TRY003)

tests/unit/query_test.py

251-251: Avoid equality comparisons to True; use query_requires_payment._is_payment_required(): for truth checks

Replace with query_requires_payment._is_payment_required()

(E712)


254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (12)
CHANGELOG.md (1)

81-81: LGTM! Clear and accurate changelog entry.

The changelog entry properly documents both the per-query and client-wide maximum payment configuration methods introduced in this PR.

tests/unit/client_test.py (1)

191-232: Excellent test coverage for set_default_max_query_payment!

The test suite comprehensively covers:

  • ✅ Valid input types (int, float, Decimal, Hbar) with correct conversion
  • ✅ Default value verification (1 Hbar)
  • ✅ Negative values raising ValueError with descriptive messages
  • ✅ Invalid types raising TypeError with descriptive messages

The tests follow unit testing best practices and will effectively protect against breaking changes.

tests/integration/contract_call_query_e2e_test.py (1)

132-179: LGTM: test validates get_cost() with manual payment setting.

This test appropriately uses set_query_payment() to manually override the payment rather than set_max_query_payment(), which is the correct pattern for validating cost calculation with explicit payment amounts.

src/hiero_sdk_python/query/query.py (2)

5-5: LGTM: Import additions support new feature.

The Decimal import is necessary for the type validation in set_max_query_payment(), and the import reorganization is clean.

Also applies to: 22-22


96-124: LGTM: Robust input validation and conversion.

The method correctly validates input types, handles negative values, and converts numeric types to Hbar. The fluent interface (returning self) is properly maintained.

Static analysis hints about long error messages (TRY003) and Union syntax (UP007) are stylistic preferences and don't impact functionality.

src/hiero_sdk_python/client/client.py (3)

5-5: LGTM: Well-defined constant with appropriate default.

The DEFAULT_MAX_QUERY_PAYMENT = Hbar(1) provides a sensible baseline for query cost protection. Imports support the type validation logic.

Also applies to: 11-11, 23-24


52-52: LGTM: Proper initialization from module constant.

Instance variable allows per-client customization while maintaining a sensible default.


249-274: LGTM: Validation logic mirrors Query.set_max_query_payment.

The implementation correctly validates input types and values, matching the pattern in Query.set_max_query_payment(). The code duplication is acceptable given it only appears in two locations and centralizing it might reduce clarity.

Static analysis hints (TRY003, UP007) are stylistic and don't impact functionality.

tests/unit/query_test.py (4)

1-2: LGTM: Necessary imports for comprehensive test coverage.

Decimal enables testing of Decimal input types, and re is needed for precise error message matching with special characters (ℏ).


60-60: LGTM: Test updated to accommodate new validation.

Setting max_query_payment to 3 Hbar when the mocked cost is 2 Hbar ensures the payment validation passes, which is the expected behavior for this test.


253-289: Excellent coverage of input validation scenarios.

The parameterized tests comprehensively validate:

  • Type conversions (int, float, Decimal, Hbar)
  • Zero and positive values
  • Negative value rejection
  • Invalid type rejection

Error message matching correctly uses type names and values.

Minor style note: Static analysis suggests using a tuple for the first argument of @pytest.mark.parametrize on line 254 (e.g., ('valid_amount', 'expected') instead of 'valid_amount,expected'), but this is cosmetic and both forms work.


291-376: Comprehensive test coverage for query/client max payment interactions.

These tests thoroughly validate:

  • Query-specific max_query_payment overriding client default
  • Fallback to client.default_max_query_payment when not set
  • Both success scenarios (cost ≤ max) and failure scenarios (cost > max)
  • Correct error message formatting

The mocked cost values ensure deterministic test behavior, and the test names clearly describe the scenarios being validated.

Comment on lines +110 to 124
query = (
ContractCallQuery()
.set_contract_id(contract_id)
.set_gas(2000000)
.set_function("getMessage")
.execute(client)
)

cost = query.get_cost(client)
query.set_max_query_payment(cost)

result = query.execute(client)

# The contract returns bytes32, which we decode to string
# This removes any padding and converts to readable text
return result.get_bytes32(0).decode("utf-8")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider simplifying the cost-fetch-and-set pattern for better UX.

The current pattern fetches the cost and immediately sets max_query_payment to that exact value. While functionally correct, this approach is somewhat redundant—setting the max to exactly the fetched cost doesn't demonstrate protection against unexpectedly high fees.

For better user experience, consider one of these alternatives:

Option 1: Set a reasonable client-wide default once at setup:

def setup_client():
    """Initialize and set up the client with operator account"""
    network = Network(network_name)
    client = Client(network)
    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)
    client.set_default_max_query_payment(Hbar(10))  # Set once for all queries
    return client

Then queries can execute directly without the cost-fetch step:

result = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .execute(client)
)

Option 2: Set a fixed reasonable max per query without fetching:

result = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .set_max_query_payment(Hbar(5))
    .execute(client)
)

Both approaches are simpler for users to copy and demonstrate the protection feature more clearly.

Comment on lines +153 to +157
if self.payment_amount > max_payment:
raise ValueError(
f"Query cost ℏ{self.payment_amount.to_hbars()} HBAR "
f"exceeds max set query payment: ℏ{max_payment.to_hbars()} HBAR"
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Simplify error message formatting.

The error message format includes redundant currency designation: "Query cost ℏ{value} HBAR" uses both the symbol (ℏ) and the word (HBAR).

Consider using Hbar's built-in string representation for consistency:

🔎 Proposed fix
             if self.payment_amount > max_payment:
                 raise ValueError(
-                    f"Query cost ℏ{self.payment_amount.to_hbars()} HBAR "
-                    f"exceeds max set query payment: ℏ{max_payment.to_hbars()} HBAR"
+                    f"Query cost {self.payment_amount} "
+                    f"exceeds max set query payment: {max_payment}"
                 )

This uses Hbar.__str__() which formats as "X.XXXXXXXX ℏ" per the Hbar class implementation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self.payment_amount > max_payment:
raise ValueError(
f"Query cost {self.payment_amount.to_hbars()} HBAR "
f"exceeds max set query payment: {max_payment.to_hbars()} HBAR"
)
if self.payment_amount > max_payment:
raise ValueError(
f"Query cost {self.payment_amount} "
f"exceeds max set query payment: {max_payment}"
)
🧰 Tools
🪛 Ruff (0.14.10)

154-157: Avoid specifying long messages outside the exception class

(TRY003)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44b253e and c773357.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • examples/contract/contract_call_query.py
  • examples/contract/contract_execute_transaction.py
  • examples/contract/ethereum_transaction.py
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/query/query.py
  • tests/integration/contract_call_query_e2e_test.py
  • tests/integration/query_e2e_test.py
  • tests/unit/client_test.py
  • tests/unit/query_test.py
🧰 Additional context used
📓 Path-based instructions (3)
examples/**/*

⚙️ CodeRabbit configuration file

examples/**/*: You are acting as a senior maintainer reviewing SDK examples. Your goal is to ensure examples work verbatim for users who copy-paste them.

Priority 1 - Correctness:

  • Verify transaction lifecycle chain (construction -> freeze_with -> sign -> execute).
  • Ensure freeze_with(client) is called BEFORE signing.
  • Validate that methods referenced actually exist in the hiero_sdk_python codebase.
  • Ensure response validation checks receipt.status against ResponseCode enums (e.g., ResponseCode.SUCCESS).

Priority 2 - Transaction Lifecycle:

  • Check method chaining logic.
  • Verify correct signing order (especially for multi-sig).
  • Ensure explicit .execute(client) calls.
  • Verify response property extraction (e.g., using .token_id, .account_id, .serial_numbers).
  • Ensure error handling uses ResponseCode(receipt.status).name for clarity.

Priority 3 - Naming & Clarity:

  • Enforce role-based naming: operator_id/_key, treasury_account_id/_key, receiver_id/_key.
  • Use _id suffix for AccountId and _key suffix for PrivateKey variables.
  • Validate negative examples explicitly check for failure codes (e.g., TOKEN_HAS_NO_PAUSE_KEY).
  • Ensure logical top-to-bottom flow without ambiguity.

Priority 4 - Consistency:

  • Verify standard patterns: def main(), if __name__ == "__main__":, load_dotenv().
  • IMPORT RULES:
    1. Accept both top-level imports (e.g., from hiero_sdk_python import PrivateKey) and fully qualified imports (e.g., from hiero_sdk_python.crypto.private_key import PrivateKey).
    2. STRICTLY validate that the import path actually exists in the project structure. Compare against other files in /examples or your knowledge of the SDK file tree.
    3. Flag hallucinations immediately (e.g., hiero_sdk_python.keys does not exist).
  • Check for try-except blocks with sys.exit(1) for critical failures.

Priority 5 - User Experience:

  • Ensure comments explain SDK usage patterns (for users,...

Files:

  • examples/contract/contract_execute_transaction.py
  • examples/contract/ethereum_transaction.py
  • examples/contract/contract_call_query.py
tests/unit/**/*

⚙️ CodeRabbit configuration file

tests/unit/**/*: You are acting as a senior maintainer reviewing unit tests for the hiero-sdk-python project. Your goal is to ensure tests are extensive, deterministic, and protect against breaking changes.

CRITICAL PRINCIPLES - Tests Must Fail Loudly & Deterministically:

  • Tests must provide useful error messages when they fail for future debugging.
  • No print() statements - use assertions with descriptive messages.
  • No timing-dependent or unseeded random assertions.
  • No network calls or external dependencies (unit tests are isolated).
  • No unjustified TODOs or skipped tests without tracking issues.

PRIORITY 1 - Protect Against Breaking Changes:

  • Assert public attributes exist (e.g., assert hasattr(obj, 'account_id')).
  • Assert return types where relevant (e.g., assert isinstance(result, AccountId)).
  • Assert fluent setters return self (e.g., assert tx.set_memo("test") is tx).
  • Assert backward-compatible defaults are maintained.
  • If a breaking change is introduced, tests must assert deprecation behavior and test old behavior until removal.

PRIORITY 2 - Constructor & Setter Behavior:

  • Test constructor behavior with valid inputs, edge cases, and invalid inputs.
  • Test setter behavior including method chaining (fluent interface).
  • Verify that setters validate input and raise appropriate exceptions.
  • Test that getters return expected values after construction/setting.

PRIORITY 3 - Comprehensive Coverage:

  • Unit tests should be extensive - test even if we don't expect users to use it currently.
  • Cover happy paths AND unhappy paths/edge cases.
  • Test boundary conditions, null/None values, empty collections, etc.
  • Avoid brittle ordering assertions unless order is part of the contract.

PRIORITY 4 - No Mocks for Non-Existent Modules:

  • All imports must reference actual SDK modules - no hallucinated paths.
  • Validate import paths against the actual src/hiero_sdk_python structure.
  • Mocks should only be used for external de...

Files:

  • tests/unit/client_test.py
  • tests/unit/query_test.py
tests/integration/**/*

⚙️ CodeRabbit configuration file

tests/integration/**/*: You are acting as a senior maintainer reviewing integration tests for the hiero-sdk-python project. Your goal is to ensure end-to-end tests validate real network behavior safely and deterministically.

CRITICAL PRINCIPLES - Safety & Diagnosability:

  • Prioritize safety: No implicit or default mainnet usage.
  • Secrets and credentials must be injected safely (env vars, not hardcoded).
  • Test failures must be diagnosable with clear error messages.
  • Tests must assert observable network behavior, not just SUCCESS.
  • Failure-path tests must assert specific ResponseCode values (e.g., TOKEN_HAS_NO_PAUSE_KEY).

PRIORITY 1 - End-to-End Behavior:

  • Tests should be end-to-end: construct → freeze → sign → execute → verify.
  • Validate resulting balances, ownership, and state changes (not just transaction success).
  • Assert transaction receipts contain expected data (IDs, serial numbers, etc.).
  • Verify network state after operations (e.g., account balance changed, token transferred).

PRIORITY 2 - Test Structure & Maintainability:

  • One major behavior per test (clear focus).
  • Tests should be readable: clear names, brief docstrings, key inline comments.
  • Minimal abstraction layers - prefer clarity over DRY.
  • Is the file too monolithic? Flag if tests should be split into smaller modules.
  • Are helper functions good candidates for pytest fixtures or shared utilities?

PRIORITY 3 - Isolation & Cleanup:

  • Local account creation over operator reuse (avoid state pollution).
  • Are accounts, tokens, and allowances properly cleaned up to avoid state leakage?
  • Recommend teardown strategies or fixture scoping improvements.
  • Tests should not depend on execution order (avoid brittle assumptions).

PRIORITY 4 - Assertions & Coverage:

  • Do tests validate only success/failure, or also assert resulting state?
  • Suggest additional assertions that would strengthen correctness (balances, allowances, ownership).
  • Cover happy paths AND ...

Files:

  • tests/integration/contract_call_query_e2e_test.py
  • tests/integration/query_e2e_test.py
🧬 Code graph analysis (8)
examples/contract/contract_execute_transaction.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
examples/contract/ethereum_transaction.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
src/hiero_sdk_python/client/client.py (1)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
tests/unit/client_test.py (2)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (2)
  • Client (32-287)
  • set_default_max_query_payment (249-274)
tests/unit/query_test.py (3)
src/hiero_sdk_python/query/query.py (4)
  • set_max_query_payment (96-124)
  • _is_payment_required (414-421)
  • get_cost (281-321)
  • _before_execute (126-157)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (1)
  • set_default_max_query_payment (249-274)
tests/integration/contract_call_query_e2e_test.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (5)
  • ContractCallQuery (27-233)
  • set_contract_id (59-67)
  • set_gas (69-77)
  • set_function (107-124)
  • execute (190-216)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
tests/integration/query_e2e_test.py (2)
src/hiero_sdk_python/query/account_info_query.py (1)
  • AccountInfoQuery (11-131)
src/hiero_sdk_python/query/query.py (1)
  • set_max_query_payment (96-124)
examples/contract/contract_call_query.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
🪛 Ruff (0.14.10)
src/hiero_sdk_python/client/client.py

7-7: typing.List is deprecated, use list instead

(UP035)


249-249: Use X | Y for type annotations

Convert to X | Y

(UP007)


263-265: Avoid specifying long messages outside the exception class

(TRY003)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

tests/unit/client_test.py

192-192: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

src/hiero_sdk_python/query/query.py

96-96: Use X | Y for type annotations

Convert to X | Y

(UP007)


113-115: Avoid specifying long messages outside the exception class

(TRY003)


119-119: Avoid specifying long messages outside the exception class

(TRY003)


154-157: Avoid specifying long messages outside the exception class

(TRY003)

tests/unit/query_test.py

251-251: Avoid equality comparisons to True; use query_requires_payment._is_payment_required(): for truth checks

Replace with query_requires_payment._is_payment_required()

(E712)


254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

tests/integration/query_e2e_test.py

255-255: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)


259-259: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build-and-test (3.13)
  • GitHub Check: build-and-test (3.11)
  • GitHub Check: build-and-test (3.10)
  • GitHub Check: build-and-test (3.12)
  • GitHub Check: run-examples
  • GitHub Check: StepSecurity Harden-Runner
🔇 Additional comments (12)
src/hiero_sdk_python/client/client.py (2)

23-24: LGTM! Reasonable default for maximum query payment.

The DEFAULT_MAX_QUERY_PAYMENT = Hbar(1) constant provides a sensible client-wide default that protects users from unexpectedly high query fees while allowing sufficient headroom for typical queries.


249-274: LGTM! Robust implementation with proper validation.

The set_default_max_query_payment() method correctly:

  • Validates input types and rejects invalid types with clear error messages
  • Ensures non-negative values for numeric inputs
  • Converts numeric types to Hbar objects consistently
  • Returns self for method chaining

The static analysis hints about Union syntax and exception message style are minor preferences for Python 3.10+ and do not affect functionality.

examples/contract/contract_execute_transaction.py (1)

110-124: [Duplicate] Reconsider the cost-fetch-and-set pattern for better user guidance.

As noted in previous review feedback, the current pattern (fetch cost → set max to exact cost → execute) is redundant and doesn't effectively demonstrate the protection feature. Setting max_query_payment to exactly the fetched cost will always pass, which defeats the purpose of showing users how to protect against unexpectedly high fees.

Consider one of these simpler, more instructive alternatives:

Option 1 (Recommended): Set a client-wide default once in setup_client():

def setup_client():
    """Initialize and set up the client with operator account"""
    network = Network(network_name)
    print(f"Connecting to Hedera {network_name} network!")
    client = Client(network)
    
    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)
    client.set_default_max_query_payment(Hbar(10))  # Set once for all queries
    print(f"Client set up with operator id {client.operator_account_id}")
    
    return client

Then queries execute directly without the cost-fetch step:

def get_contract_message(client, contract_id):
    """Get the message from the contract"""
    result = (
        ContractCallQuery()
        .set_contract_id(contract_id)
        .set_gas(2000000)
        .set_function("getMessage")
        .execute(client)
    )
    
    return result.get_bytes32(0).decode("utf-8")

Option 2: Set a fixed reasonable max per query:

def get_contract_message(client, contract_id):
    """Get the message from the contract"""
    result = (
        ContractCallQuery()
        .set_contract_id(contract_id)
        .set_gas(2000000)
        .set_function("getMessage")
        .set_max_query_payment(Hbar(5))
        .execute(client)
    )
    
    return result.get_bytes32(0).decode("utf-8")

Both approaches are simpler for users to copy-paste and better demonstrate the protection feature.

Based on past review comments and examples coding guidelines.

Likely an incorrect or invalid review comment.

examples/contract/ethereum_transaction.py (1)

146-164: LGTM! Correctly demonstrates the two-step cost-aware query pattern.

The updated get_contract_message function properly demonstrates the new workflow:

  1. Construct the query
  2. Fetch the cost with get_cost(client)
  3. Set the max payment with set_max_query_payment(cost)
  4. Execute the query

This provides a clear example for SDK users on how to use the new max query payment feature.

tests/integration/contract_call_query_e2e_test.py (1)

116-127: LGTM! Two-step pattern correctly applied.

The test properly demonstrates fetching the cost before setting the max payment and executing.

src/hiero_sdk_python/query/query.py (2)

143-157: Max payment check only applies to auto-fetched costs.

The max payment validation (lines 145-157) only triggers when self.payment_amount is None and is set via get_cost(). If a user manually sets payment_amount via set_query_payment(), the max check is bypassed entirely.

Is this intentional? If a user does:

query.set_max_query_payment(Hbar(1))
query.set_query_payment(Hbar(5))  # Exceeds max, but no validation
query.execute(client)

The query will execute without the max payment protection. Consider whether the max check should also validate manually-set payments, or document this behavior explicitly.


96-124: LGTM! Setter implementation is correct.

The set_max_query_payment method correctly:

  • Validates input types (int, float, Decimal, Hbar)
  • Rejects negative values with a clear error message
  • Converts non-Hbar inputs to Hbar
  • Returns self for method chaining
tests/unit/query_test.py (5)

328-335: Duplicate assignment removed - LGTM.

The previously flagged duplicate assignment (query_requires_payment.get_cost = mock_get_cost) appears to have been resolved. The test correctly validates that execution fails when cost exceeds the query-specific max.


366-373: Duplicate assignment removed - LGTM.

The previously flagged duplicate assignment has been resolved. The test correctly validates that execution fails when cost exceeds the client's default max when no query override exists.


291-310: LGTM! Comprehensive test for query max overriding client default.

The test correctly validates:

  1. Client has default max of 1 Hbar
  2. Query overrides with 2 Hbar
  3. Cost of 2 Hbar succeeds because it's within query's max
  4. Payment amount is correctly set

338-357: LGTM! Tests client default max fallback behavior.

The test correctly validates that when no query-level max is set, the client's default is used, and execution succeeds when cost is within that limit.


251-251: Avoid equality comparison to True.

Use the truthiness check directly instead of comparing to True.

🔎 Proposed fix
-    assert query_requires_payment._is_payment_required() == True
+    assert query_requires_payment._is_payment_required()

Likely an incorrect or invalid review comment.

Comment on lines +66 to +77
query = (
ContractCallQuery()
.set_contract_id(contract_id)
.set_gas(10000000)
.set_function("getMessage")
.execute(env.client)
)

cost = query.get_cost(env.client)
query.set_max_query_payment(cost)

result = query.execute(env.client)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add test for max_query_payment enforcement when cost exceeds limit.

The current changes demonstrate the two-step cost-aware pattern but don't validate the core protection mechanism. Setting max_query_payment to exactly the fetched cost will never trigger the "exceeds max" error path.

Consider adding a dedicated test that validates the protection behavior (per issue #1346 objective: "Fail early with a descriptive error if the cost exceeds the configured maximum"):

@pytest.mark.integration
def test_integration_contract_call_query_exceeds_max_payment(env):
    """Test that ContractCallQuery fails when cost exceeds max_query_payment."""
    # Deploy contract (reuse setup from existing tests)
    receipt = (
        FileCreateTransaction()
        .set_keys(env.operator_key.public_key())
        .set_contents(SIMPLE_CONTRACT_BYTECODE)
        .execute(env.client)
    )
    file_id = receipt.file_id
    
    receipt = (
        ContractCreateTransaction()
        .set_admin_key(env.operator_key.public_key())
        .set_gas(CONTRACT_DEPLOY_GAS)
        .set_bytecode_file_id(file_id)
        .execute(env.client)
    )
    contract_id = receipt.contract_id
    
    # Set max payment intentionally below actual cost
    query = (
        ContractCallQuery()
        .set_contract_id(contract_id)
        .set_gas(10000000)
        .set_function("greet")
        .set_max_query_payment(Hbar.from_tinybars(1))  # Intentionally too low
    )
    
    with pytest.raises(ValueError, match="exceeds max set query payment"):
        query.execute(env.client)

Comment on lines +201 to +207
def test_set_default_max_query_payment_valid_param(valid_amount, expected):
"""Test that set_default_max_query_payment correctly converts various input types to Hbar."""
client = Client.for_testnet()
# by default is 1 hbar before setting it
assert client.default_max_query_payment == Hbar(1)
client.set_default_max_query_payment(valid_amount)
assert client.default_max_query_payment == expected
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add test for fluent interface (method chaining).

Per coding guidelines, fluent setters should be tested to return self. The test verifies value conversion but doesn't assert that set_default_max_query_payment returns the client instance for method chaining.

🔎 Proposed fix
 def test_set_default_max_query_payment_valid_param(valid_amount, expected):
     """Test that set_default_max_query_payment correctly converts various input types to Hbar."""
     client = Client.for_testnet()
     # by default is 1 hbar before setting it
     assert client.default_max_query_payment == Hbar(1)
-    client.set_default_max_query_payment(valid_amount)
+    result = client.set_default_max_query_payment(valid_amount)
+    assert result is client, "set_default_max_query_payment should return self for method chaining"
     assert client.default_max_query_payment == expected
+    client.close()

As per coding guidelines: "Assert fluent setters return self".

Comment on lines +213 to +218
def test_set_default_max_query_payment_negative_value(negative_amount):
"""Test set_default_max_query_payment for negative amount values."""
client = Client.for_testnet()

with pytest.raises(ValueError, match=f"max_query_payment must be non-negative, got {negative_amount}"):
client.set_default_max_query_payment(negative_amount)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add resource cleanup in negative value tests.

The test creates a client but doesn't close it. Consider adding cleanup for consistency with other tests in this file.

🔎 Proposed fix
 def test_set_default_max_query_payment_negative_value(negative_amount):
     """Test set_default_max_query_payment for negative amount values."""
     client = Client.for_testnet()
 
     with pytest.raises(ValueError, match=f"max_query_payment must be non-negative, got {negative_amount}"):
         client.set_default_max_query_payment(negative_amount)
+    
+    client.close()

Comment on lines +224 to +232
def test_set_default_max_query_payment_invalid_param(invalid_amount):
"""Test that set_default_max_query_payment raise error for invalid param."""
client = Client.for_testnet()

with pytest.raises(TypeError, match=(
f"max_query_payment must be int, float, Decimal, or Hbar, "
f"got {type(invalid_amount).__name__}"
)):
client.set_default_max_query_payment(invalid_amount)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add resource cleanup in invalid param tests.

Same cleanup consideration as the negative value tests.

🔎 Proposed fix
 def test_set_default_max_query_payment_invalid_param(invalid_amount):
     """Test that set_default_max_query_payment raise error for invalid param."""
     client = Client.for_testnet()
 
     with pytest.raises(TypeError, match=(
         f"max_query_payment must be int, float, Decimal, or Hbar, "
         f"got {type(invalid_amount).__name__}"
     )):
         client.set_default_max_query_payment(invalid_amount)
+    
+    client.close()

Comment on lines +253 to +268
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_max_query_payment_valid_param(query, valid_amount, expected):
"""Test that set_max_query_payment correctly converts various input types to Hbar."""
# by default is none before setting it
assert query.max_query_payment is None
query.set_max_query_payment(valid_amount)
assert query.max_query_payment == expected
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add test for fluent interface (method chaining).

Per coding guidelines, fluent setters should be tested to return self. The test verifies value conversion but doesn't assert that set_max_query_payment returns the query instance.

🔎 Proposed fix
 def test_set_max_query_payment_valid_param(query, valid_amount, expected):
     """Test that set_max_query_payment correctly converts various input types to Hbar."""
     # by default is none before setting it
     assert query.max_query_payment is None
-    query.set_max_query_payment(valid_amount)
+    result = query.set_max_query_payment(valid_amount)
+    assert result is query, "set_max_query_payment should return self for method chaining"
     assert query.max_query_payment == expected

As per coding guidelines: "Assert fluent setters return self".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_max_query_payment_valid_param(query, valid_amount, expected):
"""Test that set_max_query_payment correctly converts various input types to Hbar."""
# by default is none before setting it
assert query.max_query_payment is None
query.set_max_query_payment(valid_amount)
assert query.max_query_payment == expected
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_max_query_payment_valid_param(query, valid_amount, expected):
"""Test that set_max_query_payment correctly converts various input types to Hbar."""
# by default is none before setting it
assert query.max_query_payment is None
result = query.set_max_query_payment(valid_amount)
assert result is query, "set_max_query_payment should return self for method chaining"
assert query.max_query_payment == expected
🧰 Tools
🪛 Ruff (0.14.10)

254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c773357 and 8ee657c.

📒 Files selected for processing (2)
  • tests/integration/query_e2e_test.py
  • tests/unit/query_test.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/integration/**/*

⚙️ CodeRabbit configuration file

tests/integration/**/*: You are acting as a senior maintainer reviewing integration tests for the hiero-sdk-python project. Your goal is to ensure end-to-end tests validate real network behavior safely and deterministically.

CRITICAL PRINCIPLES - Safety & Diagnosability:

  • Prioritize safety: No implicit or default mainnet usage.
  • Secrets and credentials must be injected safely (env vars, not hardcoded).
  • Test failures must be diagnosable with clear error messages.
  • Tests must assert observable network behavior, not just SUCCESS.
  • Failure-path tests must assert specific ResponseCode values (e.g., TOKEN_HAS_NO_PAUSE_KEY).

PRIORITY 1 - End-to-End Behavior:

  • Tests should be end-to-end: construct → freeze → sign → execute → verify.
  • Validate resulting balances, ownership, and state changes (not just transaction success).
  • Assert transaction receipts contain expected data (IDs, serial numbers, etc.).
  • Verify network state after operations (e.g., account balance changed, token transferred).

PRIORITY 2 - Test Structure & Maintainability:

  • One major behavior per test (clear focus).
  • Tests should be readable: clear names, brief docstrings, key inline comments.
  • Minimal abstraction layers - prefer clarity over DRY.
  • Is the file too monolithic? Flag if tests should be split into smaller modules.
  • Are helper functions good candidates for pytest fixtures or shared utilities?

PRIORITY 3 - Isolation & Cleanup:

  • Local account creation over operator reuse (avoid state pollution).
  • Are accounts, tokens, and allowances properly cleaned up to avoid state leakage?
  • Recommend teardown strategies or fixture scoping improvements.
  • Tests should not depend on execution order (avoid brittle assumptions).

PRIORITY 4 - Assertions & Coverage:

  • Do tests validate only success/failure, or also assert resulting state?
  • Suggest additional assertions that would strengthen correctness (balances, allowances, ownership).
  • Cover happy paths AND ...

Files:

  • tests/integration/query_e2e_test.py
tests/unit/**/*

⚙️ CodeRabbit configuration file

tests/unit/**/*: You are acting as a senior maintainer reviewing unit tests for the hiero-sdk-python project. Your goal is to ensure tests are extensive, deterministic, and protect against breaking changes.

CRITICAL PRINCIPLES - Tests Must Fail Loudly & Deterministically:

  • Tests must provide useful error messages when they fail for future debugging.
  • No print() statements - use assertions with descriptive messages.
  • No timing-dependent or unseeded random assertions.
  • No network calls or external dependencies (unit tests are isolated).
  • No unjustified TODOs or skipped tests without tracking issues.

PRIORITY 1 - Protect Against Breaking Changes:

  • Assert public attributes exist (e.g., assert hasattr(obj, 'account_id')).
  • Assert return types where relevant (e.g., assert isinstance(result, AccountId)).
  • Assert fluent setters return self (e.g., assert tx.set_memo("test") is tx).
  • Assert backward-compatible defaults are maintained.
  • If a breaking change is introduced, tests must assert deprecation behavior and test old behavior until removal.

PRIORITY 2 - Constructor & Setter Behavior:

  • Test constructor behavior with valid inputs, edge cases, and invalid inputs.
  • Test setter behavior including method chaining (fluent interface).
  • Verify that setters validate input and raise appropriate exceptions.
  • Test that getters return expected values after construction/setting.

PRIORITY 3 - Comprehensive Coverage:

  • Unit tests should be extensive - test even if we don't expect users to use it currently.
  • Cover happy paths AND unhappy paths/edge cases.
  • Test boundary conditions, null/None values, empty collections, etc.
  • Avoid brittle ordering assertions unless order is part of the contract.

PRIORITY 4 - No Mocks for Non-Existent Modules:

  • All imports must reference actual SDK modules - no hallucinated paths.
  • Validate import paths against the actual src/hiero_sdk_python structure.
  • Mocks should only be used for external de...

Files:

  • tests/unit/query_test.py
🧬 Code graph analysis (2)
tests/integration/query_e2e_test.py (3)
src/hiero_sdk_python/query/account_info_query.py (1)
  • AccountInfoQuery (11-131)
tests/integration/utils.py (2)
  • IntegrationTestEnv (34-96)
  • env (23-27)
src/hiero_sdk_python/query/query.py (1)
  • set_max_query_payment (96-124)
tests/unit/query_test.py (3)
src/hiero_sdk_python/query/query.py (4)
  • set_max_query_payment (96-124)
  • _is_payment_required (414-421)
  • get_cost (281-321)
  • _before_execute (126-157)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (1)
  • set_default_max_query_payment (249-274)
🪛 Ruff (0.14.10)
tests/integration/query_e2e_test.py

243-243: Redefinition of unused env from line 12

(F811)


255-255: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)


259-259: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)

tests/unit/query_test.py

251-251: Avoid equality comparisons to True; use query_requires_payment._is_payment_required(): for truth checks

Replace with query_requires_payment._is_payment_required()

(E712)


254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build-and-test (3.11)
  • GitHub Check: build-and-test (3.13)
  • GitHub Check: build-and-test (3.12)
  • GitHub Check: build-and-test (3.10)
  • GitHub Check: run-examples
  • GitHub Check: StepSecurity Harden-Runner
🔇 Additional comments (1)
tests/unit/query_test.py (1)

291-374: Good test coverage for max_query_payment behavior.

The tests comprehensively cover:

  • Query overriding client default max payment (success case)
  • Query override with cost exceeding query-specific max (error case)
  • Query using client default max payment (success case)
  • Query using client default with cost exceeding default (error case)

The use of re.escape() for matching error messages with special characters (ℏ symbol) is appropriate.

Comment on lines +242 to +260
@pytest.mark.integration
def test_integration_query_exceeds_max_payment(env):
"""Test that Query fails when cost exceeds max_query_payment."""
receipt = env.create_account(1)
account_id = receipt.id

# Set max payment below actual cost
query = (
AccountInfoQuery()
.set_account_id(account_id)
.set_max_query_payment(Hbar.from_tinybars(1)) # Intentionally too low to fail
)

with pytest.raises(ValueError) as e:
query.execute(env.client)

msg = str(e.value)
assert "Query cost" in msg and "exceeds max set query payment:" in msg

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix test implementation issues: missing match parameter and compound assertion.

The test logic is correct (using env fixture and receipt.id are appropriate based on utils.py), but there are still issues to address:

  1. Missing match parameter (Line 255): Specify a match pattern to make the test more precise.

  2. Compound assertion (Line 259): Breaking this into separate assertions improves test failure diagnosis.

🔎 Proposed fix
-    with pytest.raises(ValueError) as e:
+    with pytest.raises(ValueError, match=r"Query cost.*exceeds max set query payment") as e:
         query.execute(env.client)

     msg = str(e.value)
-    assert "Query cost" in msg and "exceeds max set query payment:" in msg
+    assert "Query cost" in msg
+    assert "exceeds max set query payment:" in msg
🧰 Tools
🪛 Ruff (0.14.10)

243-243: Redefinition of unused env from line 12

(F811)


255-255: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)


259-259: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)

query_requires_payment.set_max_query_payment(2)
assert query_requires_payment.max_query_payment == Hbar(2)

# mock the get_cost to resturn 2 hbar as required paymnet
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Fix typos in comments.

Minor typos in docstring comments.

🔎 Proposed fix
-    # mock the get_cost to resturn 2 hbar as required paymnet
+    # mock the get_cost to return 2 hbar as required payment

Apply at both lines 302 and 349.

Also applies to: 349-349

@github-actions
Copy link

github-actions bot commented Jan 6, 2026

Hi, this is MergeConflictBot.
Your pull request cannot be merged because it contains merge conflicts.

Please resolve these conflicts locally and push the changes.

To assist you, please read:

Thank you for contributing!

@aceppaluni
Copy link
Contributor

@manishdait This is looking great 👍

Please be sure to address merge conflicts. Thank you!!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ee657c and 7b9b950.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • examples/contract/contract_call_query.py
  • examples/contract/contract_execute_transaction.py
  • examples/contract/ethereum_transaction.py
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/query/query.py
  • tests/integration/contract_call_query_e2e_test.py
  • tests/integration/query_e2e_test.py
  • tests/unit/client_test.py
  • tests/unit/query_test.py
🧰 Additional context used
📓 Path-based instructions (3)
examples/**/*

⚙️ CodeRabbit configuration file

examples/**/*: You are acting as a senior maintainer reviewing SDK examples. Your goal is to ensure examples work verbatim for users who copy-paste them.

Priority 1 - Correctness:

  • Verify transaction lifecycle chain (construction -> freeze_with -> sign -> execute).
  • Ensure freeze_with(client) is called BEFORE signing.
  • Validate that methods referenced actually exist in the hiero_sdk_python codebase.
  • Ensure response validation checks receipt.status against ResponseCode enums (e.g., ResponseCode.SUCCESS).

Priority 2 - Transaction Lifecycle:

  • Check method chaining logic.
  • Verify correct signing order (especially for multi-sig).
  • Ensure explicit .execute(client) calls.
  • Verify response property extraction (e.g., using .token_id, .account_id, .serial_numbers).
  • Ensure error handling uses ResponseCode(receipt.status).name for clarity.

Priority 3 - Naming & Clarity:

  • Enforce role-based naming: operator_id/_key, treasury_account_id/_key, receiver_id/_key.
  • Use _id suffix for AccountId and _key suffix for PrivateKey variables.
  • Validate negative examples explicitly check for failure codes (e.g., TOKEN_HAS_NO_PAUSE_KEY).
  • Ensure logical top-to-bottom flow without ambiguity.

Priority 4 - Consistency:

  • Verify standard patterns: def main(), if __name__ == "__main__":, load_dotenv().
  • IMPORT RULES:
    1. Accept both top-level imports (e.g., from hiero_sdk_python import PrivateKey) and fully qualified imports (e.g., from hiero_sdk_python.crypto.private_key import PrivateKey).
    2. STRICTLY validate that the import path actually exists in the project structure. Compare against other files in /examples or your knowledge of the SDK file tree.
    3. Flag hallucinations immediately (e.g., hiero_sdk_python.keys does not exist).
  • Check for try-except blocks with sys.exit(1) for critical failures.

Priority 5 - User Experience:

  • Ensure comments explain SDK usage patterns (for users,...

Files:

  • examples/contract/ethereum_transaction.py
  • examples/contract/contract_execute_transaction.py
  • examples/contract/contract_call_query.py
tests/unit/**/*

⚙️ CodeRabbit configuration file

tests/unit/**/*: You are acting as a senior maintainer reviewing unit tests for the hiero-sdk-python project. Your goal is to ensure tests are extensive, deterministic, and protect against breaking changes.

CRITICAL PRINCIPLES - Tests Must Fail Loudly & Deterministically:

  • Tests must provide useful error messages when they fail for future debugging.
  • No print() statements - use assertions with descriptive messages.
  • No timing-dependent or unseeded random assertions.
  • No network calls or external dependencies (unit tests are isolated).
  • No unjustified TODOs or skipped tests without tracking issues.

PRIORITY 1 - Protect Against Breaking Changes:

  • Assert public attributes exist (e.g., assert hasattr(obj, 'account_id')).
  • Assert return types where relevant (e.g., assert isinstance(result, AccountId)).
  • Assert fluent setters return self (e.g., assert tx.set_memo("test") is tx).
  • Assert backward-compatible defaults are maintained.
  • If a breaking change is introduced, tests must assert deprecation behavior and test old behavior until removal.

PRIORITY 2 - Constructor & Setter Behavior:

  • Test constructor behavior with valid inputs, edge cases, and invalid inputs.
  • Test setter behavior including method chaining (fluent interface).
  • Verify that setters validate input and raise appropriate exceptions.
  • Test that getters return expected values after construction/setting.

PRIORITY 3 - Comprehensive Coverage:

  • Unit tests should be extensive - test even if we don't expect users to use it currently.
  • Cover happy paths AND unhappy paths/edge cases.
  • Test boundary conditions, null/None values, empty collections, etc.
  • Avoid brittle ordering assertions unless order is part of the contract.

PRIORITY 4 - No Mocks for Non-Existent Modules:

  • All imports must reference actual SDK modules - no hallucinated paths.
  • Validate import paths against the actual src/hiero_sdk_python structure.
  • Mocks should only be used for external de...

Files:

  • tests/unit/client_test.py
  • tests/unit/query_test.py
tests/integration/**/*

⚙️ CodeRabbit configuration file

tests/integration/**/*: You are acting as a senior maintainer reviewing integration tests for the hiero-sdk-python project. Your goal is to ensure end-to-end tests validate real network behavior safely and deterministically.

CRITICAL PRINCIPLES - Safety & Diagnosability:

  • Prioritize safety: No implicit or default mainnet usage.
  • Secrets and credentials must be injected safely (env vars, not hardcoded).
  • Test failures must be diagnosable with clear error messages.
  • Tests must assert observable network behavior, not just SUCCESS.
  • Failure-path tests must assert specific ResponseCode values (e.g., TOKEN_HAS_NO_PAUSE_KEY).

PRIORITY 1 - End-to-End Behavior:

  • Tests should be end-to-end: construct → freeze → sign → execute → verify.
  • Validate resulting balances, ownership, and state changes (not just transaction success).
  • Assert transaction receipts contain expected data (IDs, serial numbers, etc.).
  • Verify network state after operations (e.g., account balance changed, token transferred).

PRIORITY 2 - Test Structure & Maintainability:

  • One major behavior per test (clear focus).
  • Tests should be readable: clear names, brief docstrings, key inline comments.
  • Minimal abstraction layers - prefer clarity over DRY.
  • Is the file too monolithic? Flag if tests should be split into smaller modules.
  • Are helper functions good candidates for pytest fixtures or shared utilities?

PRIORITY 3 - Isolation & Cleanup:

  • Local account creation over operator reuse (avoid state pollution).
  • Are accounts, tokens, and allowances properly cleaned up to avoid state leakage?
  • Recommend teardown strategies or fixture scoping improvements.
  • Tests should not depend on execution order (avoid brittle assumptions).

PRIORITY 4 - Assertions & Coverage:

  • Do tests validate only success/failure, or also assert resulting state?
  • Suggest additional assertions that would strengthen correctness (balances, allowances, ownership).
  • Cover happy paths AND ...

Files:

  • tests/integration/query_e2e_test.py
  • tests/integration/contract_call_query_e2e_test.py
🧬 Code graph analysis (7)
examples/contract/contract_execute_transaction.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
src/hiero_sdk_python/query/query.py (1)
src/hiero_sdk_python/hbar.py (2)
  • Hbar (18-213)
  • to_hbars (67-71)
tests/unit/client_test.py (2)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
src/hiero_sdk_python/client/client.py (2)
  • Client (32-287)
  • set_default_max_query_payment (249-274)
src/hiero_sdk_python/client/client.py (1)
src/hiero_sdk_python/hbar.py (1)
  • Hbar (18-213)
examples/contract/contract_call_query.py (2)
src/hiero_sdk_python/contract/contract_call_query.py (1)
  • ContractCallQuery (27-233)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
tests/integration/contract_call_query_e2e_test.py (1)
src/hiero_sdk_python/query/query.py (2)
  • get_cost (281-321)
  • set_max_query_payment (96-124)
tests/unit/query_test.py (2)
src/hiero_sdk_python/query/query.py (4)
  • set_max_query_payment (96-124)
  • _is_payment_required (414-421)
  • get_cost (281-321)
  • _before_execute (126-157)
src/hiero_sdk_python/client/client.py (1)
  • set_default_max_query_payment (249-274)
🪛 Ruff (0.14.10)
src/hiero_sdk_python/query/query.py

96-96: Use X | Y for type annotations

Convert to X | Y

(UP007)


113-115: Avoid specifying long messages outside the exception class

(TRY003)


119-119: Avoid specifying long messages outside the exception class

(TRY003)


154-157: Avoid specifying long messages outside the exception class

(TRY003)

tests/unit/client_test.py

192-192: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

src/hiero_sdk_python/client/client.py

7-7: typing.List is deprecated, use list instead

(UP035)


249-249: Use X | Y for type annotations

Convert to X | Y

(UP007)


263-265: Avoid specifying long messages outside the exception class

(TRY003)


269-269: Avoid specifying long messages outside the exception class

(TRY003)

tests/integration/query_e2e_test.py

243-243: Redefinition of unused env from line 12

(F811)


255-255: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)


259-259: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)

tests/unit/query_test.py

251-251: Avoid equality comparisons to True; use query_requires_payment._is_payment_required(): for truth checks

Replace with query_requires_payment._is_payment_required()

(E712)


254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: run-examples
  • GitHub Check: build-and-test (3.13)
  • GitHub Check: build-and-test (3.11)
  • GitHub Check: build-and-test (3.12)
  • GitHub Check: build-and-test (3.10)
  • GitHub Check: StepSecurity Harden-Runner
🔇 Additional comments (2)
CHANGELOG.md (1)

90-90: LGTM!

The changelog entry clearly documents the new max query payment feature, including both per-query and client-wide default maximum payment methods.

examples/contract/contract_execute_transaction.py (1)

110-120: Reconsider the cost-fetch-and-set pattern for better user guidance.

Same concern as in contract_call_query.py: the current pattern (fetch cost → set max to exact cost → execute) doesn't effectively demonstrate the protection feature and adds unnecessary complexity for users.

Consider using one of these simpler alternatives:

Option 1 (Recommended): Set a client-wide default once in setup_client():

def setup_client():
    # ... existing setup code ...
    client.set_default_max_query_payment(Hbar(10))  # Set once for all queries
    return client

Then execute directly:

result = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .execute(client)
)

Option 2: Set a fixed reasonable max per query:

query = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .set_max_query_payment(Hbar(5))
)

result = query.execute(client)

Both approaches are simpler and better demonstrate how the protection feature works.

Based on examples coding guidelines.

Likely an incorrect or invalid review comment.

Comment on lines +117 to +128
query = (
ContractCallQuery()
.set_contract_id(contract_id)
.set_gas(2000000)
.set_function(
"getMessageAndOwner"
) # Call the contract's getMessageAndOwner() function
.execute(client)
)
cost = query.get_cost(client)
query.set_max_query_payment(cost)

result = query.execute(client)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Reconsider the cost-fetch-and-set pattern for better user guidance.

The current pattern (fetch cost → set max to exact cost → execute) doesn't effectively demonstrate the protection feature and adds unnecessary complexity for users who will copy this example.

Setting the max to exactly the fetched cost means the query will never fail due to cost limits, which defeats the purpose of the feature.

Consider using one of these simpler alternatives:

Option 1 (Recommended): Set a client-wide default once in setup_client():

def setup_client():
    """Initialize and set up the client with operator account"""
    network = Network(network_name)
    print(f"Connecting to Hedera {network_name} network!")
    client = Client(network)
    
    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)
    client.set_default_max_query_payment(Hbar(10))  # Set once for all queries
    print(f"Client set up with operator id {client.operator_account_id}")
    
    return client

Then execute queries directly:

result = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessageAndOwner")
    .execute(client)
)

Option 2: Set a fixed reasonable max per query:

query = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessageAndOwner")
    .set_max_query_payment(Hbar(5))
)

result = query.execute(client)

Both approaches are simpler and better demonstrate how the protection feature works.

Based on examples coding guidelines: "Examples are copied by users - prioritize explicitness over brevity."

Comment on lines +149 to +159
query = (
ContractCallQuery()
.set_contract_id(contract_id)
.set_gas(2000000)
.set_function("getMessage")
.execute(client)
)

cost = query.get_cost(client)
query.set_max_query_payment(cost)

result = query.execute(client)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Reconsider the cost-fetch-and-set pattern for better user guidance.

Same concern as in the other contract examples: the current pattern (fetch cost → set max to exact cost → execute) doesn't effectively demonstrate the protection feature and adds unnecessary complexity.

Consider using one of these simpler alternatives:

Option 1 (Recommended): Set a client-wide default once in setup_client():

def setup_client():
    # ... existing setup code ...
    client.set_default_max_query_payment(Hbar(10))
    return client

Then execute directly:

result = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .execute(client)
)

Option 2: Set a fixed reasonable max per query:

query = (
    ContractCallQuery()
    .set_contract_id(contract_id)
    .set_gas(2000000)
    .set_function("getMessage")
    .set_max_query_payment(Hbar(5))
)

result = query.execute(client)

Both approaches are simpler and better demonstrate how the protection feature works.

Based on examples coding guidelines.

Comment on lines +191 to +207
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_default_max_query_payment_valid_param(valid_amount, expected):
"""Test that set_default_max_query_payment correctly converts various input types to Hbar."""
client = Client.for_testnet()
# by default is 1 hbar before setting it
assert client.default_max_query_payment == Hbar(1)
client.set_default_max_query_payment(valid_amount)
assert client.default_max_query_payment == expected
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add fluent interface assertion and resource cleanup.

Per unit test coding guidelines (PRIORITY 1): "Assert fluent setters return self". The test verifies value conversion but doesn't assert that set_default_max_query_payment returns the client instance for method chaining.

Additionally, the test should close the client for proper resource cleanup.

🔧 Proposed fix
 def test_set_default_max_query_payment_valid_param(valid_amount, expected):
     """Test that set_default_max_query_payment correctly converts various input types to Hbar."""
     client = Client.for_testnet()
     # by default is 1 hbar before setting it
     assert client.default_max_query_payment == Hbar(1)
-    client.set_default_max_query_payment(valid_amount)
+    result = client.set_default_max_query_payment(valid_amount)
+    assert result is client, "set_default_max_query_payment should return self for method chaining"
     assert client.default_max_query_payment == expected
+    client.close()

As per coding guidelines: "Assert fluent setters return self".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_default_max_query_payment_valid_param(valid_amount, expected):
"""Test that set_default_max_query_payment correctly converts various input types to Hbar."""
client = Client.for_testnet()
# by default is 1 hbar before setting it
assert client.default_max_query_payment == Hbar(1)
client.set_default_max_query_payment(valid_amount)
assert client.default_max_query_payment == expected
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
def test_set_default_max_query_payment_valid_param(valid_amount, expected):
"""Test that set_default_max_query_payment correctly converts various input types to Hbar."""
client = Client.for_testnet()
# by default is 1 hbar before setting it
assert client.default_max_query_payment == Hbar(1)
result = client.set_default_max_query_payment(valid_amount)
assert result is client, "set_default_max_query_payment should return self for method chaining"
assert client.default_max_query_payment == expected
client.close()
🧰 Tools
🪛 Ruff (0.14.10)

192-192: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

Comment on lines +209 to +218
@pytest.mark.parametrize(
'negative_amount',
[-1, -0.1, Decimal('-0.1'), Decimal('-1')]
)
def test_set_default_max_query_payment_negative_value(negative_amount):
"""Test set_default_max_query_payment for negative amount values."""
client = Client.for_testnet()

with pytest.raises(ValueError, match=f"max_query_payment must be non-negative, got {negative_amount}"):
client.set_default_max_query_payment(negative_amount)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add resource cleanup.

The test creates a client but doesn't close it. Add cleanup for consistency with other tests in this file.

🔧 Proposed fix
 def test_set_default_max_query_payment_negative_value(negative_amount):
     """Test set_default_max_query_payment for negative amount values."""
     client = Client.for_testnet()
 
     with pytest.raises(ValueError, match=f"max_query_payment must be non-negative, got {negative_amount}"):
         client.set_default_max_query_payment(negative_amount)
+    
+    client.close()

Comment on lines +220 to +232
@pytest.mark.parametrize(
'invalid_amount',
['1', 'abc', None, object()]
)
def test_set_default_max_query_payment_invalid_param(invalid_amount):
"""Test that set_default_max_query_payment raise error for invalid param."""
client = Client.for_testnet()

with pytest.raises(TypeError, match=(
f"max_query_payment must be int, float, Decimal, or Hbar, "
f"got {type(invalid_amount).__name__}"
)):
client.set_default_max_query_payment(invalid_amount)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add resource cleanup.

Same cleanup consideration as the negative value tests.

🔧 Proposed fix
 def test_set_default_max_query_payment_invalid_param(invalid_amount):
     """Test that set_default_max_query_payment raise error for invalid param."""
     client = Client.for_testnet()
 
     with pytest.raises(TypeError, match=(
         f"max_query_payment must be int, float, Decimal, or Hbar, "
         f"got {type(invalid_amount).__name__}"
     )):
         client.set_default_max_query_payment(invalid_amount)
+    
+    client.close()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize(
'invalid_amount',
['1', 'abc', None, object()]
)
def test_set_default_max_query_payment_invalid_param(invalid_amount):
"""Test that set_default_max_query_payment raise error for invalid param."""
client = Client.for_testnet()
with pytest.raises(TypeError, match=(
f"max_query_payment must be int, float, Decimal, or Hbar, "
f"got {type(invalid_amount).__name__}"
)):
client.set_default_max_query_payment(invalid_amount)
@pytest.mark.parametrize(
'invalid_amount',
['1', 'abc', None, object()]
)
def test_set_default_max_query_payment_invalid_param(invalid_amount):
"""Test that set_default_max_query_payment raise error for invalid param."""
client = Client.for_testnet()
with pytest.raises(TypeError, match=(
f"max_query_payment must be int, float, Decimal, or Hbar, "
f"got {type(invalid_amount).__name__}"
)):
client.set_default_max_query_payment(invalid_amount)
client.close()

assert query._is_payment_required() == True
# Verify that payment-requiring query also defaults to requiring payment
assert query_requires_payment._is_payment_required() == True
assert query_requires_payment._is_payment_required() == True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use direct truth check instead of equality comparison to True.

Per Python best practices and static analysis (E712), use direct truth check for boolean returns.

♻️ Proposed fix
-    assert query_requires_payment._is_payment_required() == True
+    assert query_requires_payment._is_payment_required()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert query_requires_payment._is_payment_required() == True
assert query_requires_payment._is_payment_required()
🧰 Tools
🪛 Ruff (0.14.10)

251-251: Avoid equality comparisons to True; use query_requires_payment._is_payment_required(): for truth checks

Replace with query_requires_payment._is_payment_required()

(E712)

Comment on lines +253 to +262
@pytest.mark.parametrize(
'valid_amount,expected',
[
(1, Hbar(1)),
(0.1, Hbar(0.1)),
(Decimal('0.1'), Hbar(Decimal('0.1'))),
(Hbar(1), Hbar(1)),
(Hbar(0), Hbar(0))
]
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use tuple for pytest.mark.parametrize first argument.

Per pytest conventions (PT006), the first argument should be a tuple of parameter names.

♻️ Proposed fix
 @pytest.mark.parametrize(
-    'valid_amount,expected',
+    ('valid_amount', 'expected'),
     [
         (1, Hbar(1)),
         (0.1, Hbar(0.1)),
         (Decimal('0.1'), Hbar(Decimal('0.1'))),
         (Hbar(1), Hbar(1)),
         (Hbar(0), Hbar(0))
     ]  
 )

Apply the same fix to lines 270-272 and 279-281.

🧰 Tools
🪛 Ruff (0.14.10)

254-254: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple

Use a tuple for the first argument

(PT006)

Comment on lines +263 to +268
def test_set_max_query_payment_valid_param(query, valid_amount, expected):
"""Test that set_max_query_payment correctly converts various input types to Hbar."""
# by default is none before setting it
assert query.max_query_payment is None
query.set_max_query_payment(valid_amount)
assert query.max_query_payment == expected
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add assertion for fluent interface (method chaining).

Per unit test coding guidelines: "Assert fluent setters return self". The test verifies value conversion but doesn't validate that set_max_query_payment returns the query instance for method chaining.

♻️ Proposed fix
 def test_set_max_query_payment_valid_param(query, valid_amount, expected):
     """Test that set_max_query_payment correctly converts various input types to Hbar."""
     # by default is none before setting it
     assert query.max_query_payment is None
-    query.set_max_query_payment(valid_amount)
+    result = query.set_max_query_payment(valid_amount)
+    assert result is query, "set_max_query_payment should return self for method chaining"
     assert query.max_query_payment == expected

query_requires_payment.set_max_query_payment(2)
assert query_requires_payment.max_query_payment == Hbar(2)

# mock the get_cost to return 2 hbar as required paymnet
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Fix typos in comments.

Minor typos: "resturn" should be "return", "paymnet" should be "payment".

♻️ Proposed fix
-    # mock the get_cost to return 2 hbar as required paymnet
+    # mock the get_cost to return 2 hbar as required payment

Also applies to line 349.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# mock the get_cost to return 2 hbar as required paymnet
# mock the get_cost to return 2 hbar as required payment

@exploreriii exploreriii requested a review from a team January 8, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add max_query_payment support in Python SDK Query base class

2 participants