Skip to content

Commit 6ac3e4e

Browse files
authored
feat: Add optional receipt waiting to Transaction.execute() (hiero-ledger#1769)
Signed-off-by: Manish Dait <daitmanish88@gmail.com>
1 parent 08af338 commit 6ac3e4e

11 files changed

Lines changed: 1191 additions & 43 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
2525
- Fix `TopicInfo.__str__()` to format `expiration_time` in UTC so unit tests pass in non-UTC environments. (#1800)
2626
- Resolve CodeQL `reflected-XSS` warning in TCK JSON-RPC endpoint
2727
- Improve `keccak256` docstring formatting for better readability and consistency (#1624)
28+
- Added `wait_for_receipt` parameter for `Transaction.execute()` to support optional receipt waiting, and `get_receipt_query`, `get_record_query` and `get_record` to `TransactionResponse`.
2829

2930
### Examples
3031
- Refactor `examples/file/file_create_transaction.py` to remove `os`,`dotenv`,`AccountId`,`PrivateKey`,`Network` imports that are no longer needed and updated setup-client() (#1610)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
Demonstrate creating, executing, and retrieving an account creation transaction
3+
without immediately waiting for the receipt.
4+
5+
uv run examples/transaction/transaction_without_wait_for_receipt.py
6+
python examples/transaction/transaction_without_wait_for_receipt.py
7+
"""
8+
9+
import sys
10+
11+
from hiero_sdk_python import (
12+
Client,
13+
AccountCreateTransaction,
14+
PrivateKey,
15+
ResponseCode
16+
)
17+
18+
19+
def build_transaction():
20+
"""
21+
Build a new AccountCreateTransaction with a generated private key
22+
and a minimal initial balance.
23+
"""
24+
key = PrivateKey.generate()
25+
return AccountCreateTransaction().set_key_without_alias(key).set_initial_balance(1)
26+
27+
28+
def print_transaction_record(record):
29+
"""Print the transaction record."""
30+
print(f"Transaction ID: {record.transaction_id}")
31+
print(f"Transaction Fee: {record.transaction_fee}")
32+
print(f"Transaction Hash: {record.transaction_hash.hex()}")
33+
print(f"Transaction Memo: {record.transaction_memo}")
34+
print(f"Transaction Account ID: {record.receipt.account_id}")
35+
36+
37+
def print_transaction_receipt(receipt):
38+
"""Print the transaction receipt."""
39+
if receipt.status != ResponseCode.SUCCESS:
40+
raise RuntimeError(f"Receipt Query failed with status: {ResponseCode(receipt.status).name}")
41+
42+
print(f"Transaction Receipt Status: {ResponseCode(receipt.status).name}")
43+
print(f"Transaction Account ID: {receipt.account_id}")
44+
45+
46+
def main():
47+
"""
48+
1. Initialize a client from environment variables (operator required in env).
49+
2. Build an AccountCreateTransaction.
50+
3. Execute the transaction (without waiting for receipt).
51+
4. Retrieve the receipt and record after execution.
52+
"""
53+
try:
54+
client = Client.from_env()
55+
tx = build_transaction()
56+
57+
print("Executing transaction...")
58+
# Execute the transaction without waiting for receipt immediately
59+
response = tx.execute(client, wait_for_receipt=False)
60+
61+
print("Transaction executed successfully!")
62+
print(f"Transaction submitted with ID: {response.transaction_id}")
63+
64+
# Retrieve receipt and record after submission
65+
print("\n1. Getting Transaction Receipt using Transaction Response...")
66+
receipt = response.get_receipt(client)
67+
print_transaction_receipt(receipt)
68+
69+
70+
print("\n2. Getting Transaction Record using Transaction Response...")
71+
record = response.get_record(client)
72+
print_transaction_record(record)
73+
74+
except Exception as err:
75+
print(f"Error during transaction: {err}")
76+
sys.exit(1)
77+
78+
79+
if __name__ == "__main__":
80+
main()

src/hiero_sdk_python/consensus/topic_message_submit_transaction.py

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import math
2-
from typing import List, Optional, Union
2+
from typing import List, Literal, Optional, Union, overload
33
from hiero_sdk_python.client.client import Client
44
from hiero_sdk_python.consensus.topic_id import TopicId
55
from hiero_sdk_python.crypto.private_key import PrivateKey
@@ -13,6 +13,8 @@
1313
from hiero_sdk_python.channels import _Channel
1414
from hiero_sdk_python.executable import _Method
1515
from hiero_sdk_python.transaction.transaction_id import TransactionId
16+
from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt
17+
from hiero_sdk_python.transaction.transaction_response import TransactionResponse
1618

1719

1820
class TopicMessageSubmitTransaction(Transaction):
@@ -282,25 +284,92 @@ def freeze_with(self, client: "Client") -> "TopicMessageSubmitTransaction":
282284
self._transaction_ids.append(chunk_transaction_id)
283285

284286
return super().freeze_with(client)
287+
288+
@overload
289+
def execute(
290+
self,
291+
client: "Client",
292+
timeout: int | float | None = None,
293+
wait_for_receipt: Literal[True] = True,
294+
) -> "TransactionReceipt":
295+
...
296+
297+
@overload
298+
def execute(
299+
self,
300+
client: "Client",
301+
timeout: int | float | None = None,
302+
wait_for_receipt: Literal[False] = False,
303+
) -> "TransactionResponse":
304+
...
285305

286-
287-
def execute(self, client: "Client", timeout: Optional[Union[int, float]] = None):
306+
def execute(
307+
self,
308+
client: "Client",
309+
timeout: int | float | None = None,
310+
wait_for_receipt: bool = True
311+
) -> TransactionReceipt | TransactionResponse:
288312
"""
289313
Executes the topic message submit transaction.
290314
291-
For multi-chunk transactions, this method will execute all chunks sequentially.
315+
For multi-chunk transactions, this method will execute all chunks sequentially and return first response.
292316
293317
Args:
294318
client: The client to execute the transaction with.
295-
timeout (Optional[Union[int, float]): The total execution timeout (in seconds) for this execution.
319+
timeout (int | float | None, optional): The total execution timeout (in seconds) for this execution.
320+
wait_for_receipt (bool, optional): Whether to wait for consensus and return the receipt.
321+
If False, the method returns a TransactionResponse immediately after submission.
296322
297323
Returns:
298-
TransactionReceipt: The receipt from the first chunk execution.
324+
TransactionReceipt: If wait_for_receipt is True (default)
325+
TransactionResponse: If wait_for_receipt is False
326+
"""
327+
# Return the first response as the JS SDK does
328+
return self.execute_all(client, timeout, wait_for_receipt)[0]
329+
330+
@overload
331+
def execute_all(
332+
self,
333+
client: "Client",
334+
timeout: int | float | None = None,
335+
wait_for_receipt: Literal[True] = True,
336+
) -> List["TransactionReceipt"]:
337+
...
338+
339+
@overload
340+
def execute_all(
341+
self,
342+
client: "Client",
343+
timeout: int | float | None = None,
344+
wait_for_receipt: Literal[False] = False,
345+
) -> List["TransactionResponse"]:
346+
...
347+
348+
def execute_all(
349+
self,
350+
client: "Client",
351+
timeout: int | float | None = None,
352+
wait_for_receipt: bool = True
353+
) -> List["TransactionReceipt"] | List["TransactionResponse"]:
354+
"""
355+
Executes the topic message submit transaction.
356+
357+
This method will execute all chunks sequentially and return list of all responses.
358+
359+
Args:
360+
client: The client to execute the transaction with.
361+
timeout (int | float | None, optional): The total execution timeout (in seconds) for this execution.
362+
wait_for_receipt (bool, optional): Whether to wait for consensus and return the receipt.
363+
If False, the method returns a TransactionResponse immediately after submission.
364+
365+
Returns:
366+
List[TransactionReceipt]: If wait_for_receipt is True (default)
367+
List[TransactionResponse]: If wait_for_receipt is False
299368
"""
300369
self._validate_chunking()
301370

302371
if self.get_required_chunks() == 1:
303-
return super().execute(client, timeout)
372+
return [super().execute(client, timeout, wait_for_receipt)]
304373

305374
# Multi-chunk transaction - execute all chunks
306375
responses = []
@@ -320,11 +389,11 @@ def execute(self, client: "Client", timeout: Optional[Union[int, float]] = None)
320389
super().sign(signing_key)
321390

322391
# Execute the chunk
323-
response = super().execute(client, timeout)
392+
response = super().execute(client, timeout, wait_for_receipt)
324393
responses.append(response)
325-
326-
# Return the first response as the JS SDK does
327-
return responses[0] if responses else None
394+
395+
return responses
396+
328397

329398
def sign(self, private_key: "PrivateKey"):
330399
"""

src/hiero_sdk_python/file/file_append_transaction.py

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"""
1515

1616
import math
17-
from typing import TYPE_CHECKING, Any, List, Optional, Union
17+
from typing import TYPE_CHECKING, Any, List, Literal, Optional, Union, overload
1818
from hiero_sdk_python.file.file_id import FileId
1919
from hiero_sdk_python.hbar import Hbar
2020
from hiero_sdk_python.transaction.transaction import Transaction
@@ -27,10 +27,11 @@
2727
# Use TYPE_CHECKING to avoid circular import errors
2828
if TYPE_CHECKING:
2929
from hiero_sdk_python.client import Client
30-
from hiero_sdk_python.keys import PrivateKey
30+
from hiero_sdk_python.crypto.private_key import PrivateKey
3131
from hiero_sdk_python.channels import _Channel
3232
from hiero_sdk_python.executable import _Method
33-
from hiero_sdk_python.transaction_receipt import TransactionReceipt
33+
from hiero_sdk_python.transaction.transaction import TransactionReceipt
34+
from hiero_sdk_python.transaction.transaction_response import TransactionResponse
3435

3536

3637
# pylint: disable=too-many-instance-attributes
@@ -320,25 +321,92 @@ def freeze_with(self, client: "Client") -> FileAppendTransaction:
320321

321322
return self
322323

323-
324-
def execute(self, client: "Client", timeout: Optional[Union[int, float]] = None) -> Any:
324+
@overload
325+
def execute(
326+
self,
327+
client: "Client",
328+
timeout: int | float | None = None,
329+
wait_for_receipt: Literal[True] = True,
330+
) -> "TransactionReceipt":
331+
...
332+
333+
@overload
334+
def execute(
335+
self,
336+
client: "Client",
337+
timeout: int | float | None = None,
338+
wait_for_receipt: Literal[False] = False,
339+
) -> "TransactionResponse":
340+
...
341+
342+
def execute(
343+
self,
344+
client: "Client",
345+
timeout: int | float | None = None,
346+
wait_for_receipt: bool = True
347+
) -> "TransactionReceipt" | "TransactionResponse":
348+
"""
349+
Executes the file append transaction.
350+
351+
For multi-chunk transactions, this method will execute all chunks sequentially and return first response.
352+
353+
Args:
354+
client: The client to execute the transaction with.
355+
timeout (int | float | None, optional): The total execution timeout (in seconds) for this execution.
356+
wait_for_receipt (bool, optional): Whether to wait for consensus and return the receipt.
357+
If False, the method returns a TransactionResponse immediately after submission.
358+
359+
Returns:
360+
TransactionReceipt: If wait_for_receipt is True (default)
361+
TransactionResponse: If wait_for_receipt is False
362+
"""
363+
# Return the first response (as per JavaScript implementation)
364+
return self.execute_all(client, timeout, wait_for_receipt)[0]
365+
366+
@overload
367+
def execute_all(
368+
self,
369+
client: "Client",
370+
timeout: int | float | None = None,
371+
wait_for_receipt: Literal[True] = True,
372+
) -> List["TransactionReceipt"]:
373+
...
374+
375+
@overload
376+
def execute_all(
377+
self,
378+
client: "Client",
379+
timeout: int | float | None = None,
380+
wait_for_receipt: Literal[False] = False,
381+
) -> List["TransactionResponse"]:
382+
...
383+
384+
def execute_all(
385+
self,
386+
client: "Client",
387+
timeout: int | float | None = None,
388+
wait_for_receipt: bool = True
389+
) -> List["TransactionReceipt"] | List["TransactionResponse"]:
325390
"""
326391
Executes the file append transaction.
327392
328-
For multi-chunk transactions, this method will execute all chunks sequentially.
393+
This method will execute all chunks sequentially and return list of response for each chunked
329394
330395
Args:
331396
client: The client to execute the transaction with.
332-
timeout (Optional[Union[int, float]): The total execution timeout (in seconds) for this execution.
397+
timeout (int | float | None, optional): The total execution timeout (in seconds) for this execution.
398+
wait_for_receipt (bool, optional): Whether to wait for consensus and return the receipt.
399+
If False, the method returns a TransactionResponse immediately after submission.
333400
334401
Returns:
335-
TransactionReceipt: The receipt from the first chunk execution.
402+
List[TransactionReceipt]: If wait_for_receipt is True (default)
403+
List[TransactionResponse]: If wait_for_receipt is False
336404
"""
337405
self._validate_chunking()
338406

339407
if self.get_required_chunks() == 1:
340408
# Single chunk transaction
341-
return super().execute(client, timeout)
409+
return [super().execute(client, timeout, wait_for_receipt)]
342410

343411
# Multi-chunk transaction - execute all chunks
344412
responses = []
@@ -362,11 +430,10 @@ def execute(self, client: "Client", timeout: Optional[Union[int, float]] = None)
362430
super().sign(signing_key)
363431

364432
# Execute the chunk
365-
response = super().execute(client, timeout)
433+
response = super().execute(client, timeout, wait_for_receipt)
366434
responses.append(response)
367435

368-
# Return the first response (as per JavaScript implementation)
369-
return responses[0] if responses else None
436+
return responses
370437

371438
def sign(self, private_key: "PrivateKey") -> FileAppendTransaction:
372439
"""

0 commit comments

Comments
 (0)