From 1f6e3602b1f10e829f29ed7bb25ef167af0ef39a Mon Sep 17 00:00:00 2001 From: Ntege Daniel Date: Sat, 23 May 2026 01:27:09 +0300 Subject: [PATCH 1/2] feat: add freeze_with method to TopicCreateTransaction for auto-renew account handling Signed-off-by: Ntege Daniel --- .../consensus/topic_create_transaction.py | 27 +++++++++ tck/handlers/topic.py | 3 - tests/unit/topic_create_transaction_test.py | 56 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/hiero_sdk_python/consensus/topic_create_transaction.py b/src/hiero_sdk_python/consensus/topic_create_transaction.py index 45083c8be..1eedc813d 100644 --- a/src/hiero_sdk_python/consensus/topic_create_transaction.py +++ b/src/hiero_sdk_python/consensus/topic_create_transaction.py @@ -9,6 +9,8 @@ from __future__ import annotations +from typing import TYPE_CHECKING + from hiero_sdk_python.account.account_id import AccountId from hiero_sdk_python.channels import _Channel from hiero_sdk_python.crypto.key import Key @@ -23,6 +25,10 @@ from hiero_sdk_python.utils.key_utils import key_to_proto +if TYPE_CHECKING: + from hiero_sdk_python.client.client import Client + + class TopicCreateTransaction(Transaction): """ Represents a transaction to create a new topic in the Hedera @@ -187,6 +193,27 @@ def set_fee_exempt_keys(self, keys: list[Key]) -> TopicCreateTransaction: self.fee_exempt_keys = keys return self + def freeze_with(self, client: Client) -> TopicCreateTransaction: + """ + Freeze the transaction with the given client. + + If `auto_renew_account` was not explicitly set, automatically assigns it: + - If `transaction_id` is already set, use its `account_id`. + - Otherwise, fall back to the client's `operator_account_id`. + + Args: + client (Client): The client instance to use for setting defaults. + + Returns: + TopicCreateTransaction: The current transaction instance for method chaining. + """ + if client is not None and client.operator_account_id is not None and self.auto_renew_account is None: + self.auto_renew_account = ( + self.transaction_id.account_id if self.transaction_id is not None else client.operator_account_id + ) + + return super().freeze_with(client) + def _to_proto_key(self, key: Key | None): """ Backwards-compatible wrapper around `key_to_proto` for converting SDK keys diff --git a/tck/handlers/topic.py b/tck/handlers/topic.py index 77aca2742..4f64d7ccb 100644 --- a/tck/handlers/topic.py +++ b/tck/handlers/topic.py @@ -73,9 +73,6 @@ def create_topic(params: CreateTopicParams) -> CreateTopicResponse: transaction = _build_create_topic_transaction(params) - if params.autoRenewAccountId is None and client is not None and client.operator_account_id is not None: - transaction.set_auto_renew_account(client.operator_account_id) - if params.commonTransactionParams is not None: params.commonTransactionParams.apply_common_params(transaction, client) diff --git a/tests/unit/topic_create_transaction_test.py b/tests/unit/topic_create_transaction_test.py index 4c92ce28e..ce21872c4 100644 --- a/tests/unit/topic_create_transaction_test.py +++ b/tests/unit/topic_create_transaction_test.py @@ -23,6 +23,7 @@ from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.tokens.custom_fixed_fee import CustomFixedFee from hiero_sdk_python.tokens.token_id import TokenId +from hiero_sdk_python.transaction.transaction_id import TransactionId from tests.unit.mock_server import mock_hedera_servers @@ -574,3 +575,58 @@ def test_mixed_key_types_in_constructor(mock_account_ids): assert transaction_body.consensusCreateTopic.submitKey.ed25519 == ed25519_public.to_bytes_raw() assert transaction_body.consensusCreateTopic.fee_schedule_key.HasField("ECDSA_secp256k1") assert len(transaction_body.consensusCreateTopic.fee_exempt_key_list) == 2 + + +def _generate_transaction_id(account_id): + """Generate a unique transaction ID based on the account ID and the current timestamp.""" + import time + + from hiero_sdk_python.hapi.services import timestamp_pb2 + + current_time = time.time() + timestamp_seconds = int(current_time) + timestamp_nanos = int((current_time - timestamp_seconds) * 1e9) + + tx_timestamp = timestamp_pb2.Timestamp(seconds=timestamp_seconds, nanos=timestamp_nanos) + return TransactionId(valid_start=tx_timestamp, account_id=account_id) + + +def test_freeze_with_defaults_auto_renew_account_to_operator(mock_client): + """Test that freeze_with sets auto_renew_account to operator when not explicitly set.""" + tx = TopicCreateTransaction(memo="test topic") + frozen_tx = tx.freeze_with(mock_client) + body = frozen_tx.build_transaction_body() + + assert body.consensusCreateTopic.autoRenewAccount == mock_client.operator_account_id._to_proto() + + +def test_freeze_with_preserves_explicit_auto_renew_account(mock_client): + """Test that freeze_with does not override an explicitly set auto_renew_account.""" + explicit_account = AccountId(0, 0, 9999) + tx = TopicCreateTransaction(memo="test topic", auto_renew_account=explicit_account) + frozen_tx = tx.freeze_with(mock_client) + body = frozen_tx.build_transaction_body() + + assert body.consensusCreateTopic.autoRenewAccount == explicit_account._to_proto() + + +def test_freeze_with_uses_transaction_id_account_when_set(mock_client): + """Test that freeze_with uses transaction_id.account_id when transaction_id is set.""" + tx_account = AccountId(0, 0, 5555) + tx = TopicCreateTransaction(memo="test topic") + tx.transaction_id = _generate_transaction_id(tx_account) + frozen_tx = tx.freeze_with(mock_client) + body = frozen_tx.build_transaction_body() + + assert body.consensusCreateTopic.autoRenewAccount == tx_account._to_proto() + + +def test_freeze_with_auto_renew_account_set_via_setter(mock_client): + """Test that freeze_with preserves auto_renew_account set via set_auto_renew_account.""" + explicit_account = AccountId(0, 0, 7777) + tx = TopicCreateTransaction(memo="test topic") + tx.set_auto_renew_account(explicit_account) + frozen_tx = tx.freeze_with(mock_client) + body = frozen_tx.build_transaction_body() + + assert body.consensusCreateTopic.autoRenewAccount == explicit_account._to_proto() From b902aef3faaec791a6550d75516917c22352a00c Mon Sep 17 00:00:00 2001 From: Ntege Daniel Date: Sat, 23 May 2026 02:37:23 +0300 Subject: [PATCH 2/2] feat: add freeze_with method to TopicCreateTransaction for auto-renew account handling Signed-off-by: Ntege Daniel --- tests/unit/topic_create_transaction_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/topic_create_transaction_test.py b/tests/unit/topic_create_transaction_test.py index ce21872c4..f555c3e08 100644 --- a/tests/unit/topic_create_transaction_test.py +++ b/tests/unit/topic_create_transaction_test.py @@ -630,3 +630,14 @@ def test_freeze_with_auto_renew_account_set_via_setter(mock_client): body = frozen_tx.build_transaction_body() assert body.consensusCreateTopic.autoRenewAccount == explicit_account._to_proto() + + +def test_freeze_with_leaves_auto_renew_account_unset_without_operator(mock_client): + """Test that freeze_with leaves auto_renew_account unset when operator_account_id is None.""" + mock_client.operator_account_id = None + tx = TopicCreateTransaction(memo="test topic") + tx.transaction_id = _generate_transaction_id(AccountId(0, 0, 1234)) + frozen_tx = tx.freeze_with(mock_client) + body = frozen_tx.build_transaction_body() + + assert not body.consensusCreateTopic.HasField("autoRenewAccount")